Specification Testing with EF - The ObjectContext instance has been disposed - c#

I have the following SpecFlow scenario:
[When(#"the registration is submitted")]
public void WhenTheRegistrationIsSubmitted()
{
//var controller = _kernel.Get<AccountController>();
var factory = new HockeyDbContextFactory();
var userRepository = new Repository<User>(factory);
var cryptoService = new CryptoService();
var roleRepository = new Repository<Role>(factory);
var playerService = new Mock<IPlayerService>();
var leagueService = new Mock<ILeagueService>();
var userService = new UserService(userRepository, cryptoService, roleRepository);
var controller = new AccountController(userService, playerService.Object, leagueService.Object);
controller.Register(_registerModel);
}
Which eventually calls the following method through my controller:
public void RegisterUser(User user)
{
var salt = _cryptoService.GenerateSalt();
var hasedPassword = _cryptoService.HashPassword(user.Password, salt);
user.PasswordSalt = salt;
user.Password = hasedPassword;
var defaultRole = _roleRepository.GetAll().Single(x => x.RoleName == "User");
user.Roles.Add(defaultRole);
Insert(user);
}
All of my database calls are fine until I get to this line:
var defaultRole = _roleRepository.GetAll().Single(x => x.RoleName == "User");
When I breakpoint on that line and inspect the call to GetAll(), I have context and I can view the query. The exception occurs on the call to Single(). Now, if I stick a .Include(x => x.Users) on the call to GetAll(), I'm fine. This tells me it has something to do with lazy-loading.
The error i get is: error: The ObjectContext instance has been disposed and can no longer be used for operations that require a connection.
When RegisterUser is called from my web application, I'm fine. When RegisterUser is called from my specification test, it fails. Does anyone have some incite?
UPDATE:
To add a little more information, here is the controller action being called:
[HttpPost]
[AllowAnonymous]
public ActionResult Register(RegisterModel model)
{
if (!_userService.EmailIsUnique(model.EmailAddress))
ModelState.AddModelError("EmailAddress", "Email Address is already in use.");
if (!_userService.UserNameIsUnique(model.UserName))
ModelState.AddModelError("UserName", "User Name is already in use");
if (ModelState.IsValid)
{
// Attempt to register the user
try
{
var user = Mapper.Map<User>(model);
_userService.RegisterUser(user);
FormsAuthentication.SetAuthCookie(model.UserName, false);
return View("RegisterSuccess");
}
catch (MembershipCreateUserException e)
{
ModelState.AddModelError("", ErrorCodeToString(e.StatusCode));
}
}
// If we got this far, something failed, redisplay form
return View(model);
}
stepping through the code, I never make it to FormsAuthentication.SetAuthCookie(model.UserName, false);

I figured out what the issue was. I was seeding my test database with this step:
[BeforeFeature]
public static void BeforeFeature()
{
MappingConfig.RegisterMappings();
Database.SetInitializer(new TestDatabaseInitializer());
}
The context in my TestDatabaseInitializer must have been conflicting with the context I created in my scenario. Thanks for the comment Gert, it gave me the idea to take a closer look at what was going on in the rest of my scenario.

Related

Mock returns null when an object is expected

I have a test that looks like this.
namespace Domain.Tests.Unit.Features.ForUser.Auth
{
[TestClass]
public class LoginHandlerTests
{
[TestMethod]
public async Task Should_Succeede_With_Valid_User()
{
// Arrange
var loginCommand = new LoginHandler.LoginCommand
{
Email = "testemail",
Password = "testpassword"
};
var user = new User
{
Email = loginCommand.Email,
UserName = "testname",
};
var userServiceMock = new UserServiceFixture()
.WithSucceededFindByEmailAsync(loginCommand.Email)
.WithSucceededGetRolesAsync(user)
.GetMock();
// Problematic MOCK
var result = new Mock<ISignInServiceResult>()
.SetupProperty(l => l.Succeeded, true);
var signInServiceMock = new Mock<ISignInService>();
signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(user, loginCommand.Password))
.Returns(Task.FromResult(result.Object));
var jwtGeneratorServiceMock = new JwtGeneratorServiceFixture()
.WithSucceededGeneration(loginCommand.Email, new string[] { "User" })
.GetMock();
// Here the result is what i expect
//var result1 = await signInServiceMock.Object.CheckPasswordSignInAsync(user, loginCommand.Password);
// Act
var sut = new LoginHandler.Handler(
userServiceMock.Object,
signInServiceMock.Object,
jwtGeneratorServiceMock.Object
);
var loginResponse = await sut.Handle(loginCommand, new CancellationToken());
// Assert
loginResponse.Should().NotBeNull();
loginResponse.Success.Should().BeTrue();
loginResponse.Token.Should().NotBeEmpty();
loginResponse.RefreshToken.Should().NotBeEmpty();
The only problem is that signInServiceMock returns null when the method is called on my handler. If I call it directly on my test i recieve the expected result. But when its called on my handler it always returns null.
I have checked the setup method and the params needed, all seems correct. Any idea? Thanks
The handler is this:
public class Handler : IRequestHandler<LoginCommand, LoginResponse>
{
private readonly IUserService _userService;
private readonly ISignInService _signInService;
private readonly IJwtGeneratorService _jwtGeneratorService;
public Handler(IUserService userService, ISignInService signInService, IJwtGeneratorService jwtGeneratorService)
{
_userService = userService;
_signInService = signInService;
_jwtGeneratorService = jwtGeneratorService;
}
public async Task<LoginResponse> Handle(LoginCommand command, CancellationToken _cancellationToken)
{
var user = await _userService.FindByEmailAsync(command.Email);
if (user is null) throw new InvalidLoginCredentialsException();
ISignInServiceResult checkedPassword
= await _signInService.CheckPasswordSignInAsync(user, command.Password);
if (!checkedPassword.Succeeded) throw new InvalidLoginCredentialsException();
var roles = await _userService.GetRolesAsync(user);
if (roles is null)
throw new UnableToGetRolesException();
ITokenData? token = _jwtGeneratorService.Generate(user.Email, roles);
if (token is null)
throw new LoginTokenGenerationException();
return new LoginResponse
{
Success = true,
Token = token.Token,
RefreshToken = token.RefreshToken
};
}
}
The problem is that your mock setup is not correctly aligned with how your Handler internally uses it.
In your setup you have this:
var user = new User
{
Email = loginCommand.Email,
UserName = "testname",
};
signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(user, loginCommand.Password))
.Returns(Task.FromResult(result.Object));
That instructs the mock to return the desired result only upon receiving this exact user instance, while internally in your Handler you're creating another User instance and passing it to the mocked method as follows:
// new User is being created:
var user = await _userService.FindByEmailAsync(command.Email);
// No longer matches your setup:
await _signInService.CheckPasswordSignInAsync(user, command.Password);
Perhaps you meant this, instead:
signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(It.IsAny<User>(), loginCommand.Password))
Update:
Actually, the real problem probably lies in a missing setup of your UserService mock.
Although you're calling .WithSucceededGetRolesAsync(user), I believe it doesn't affect the eventual call to FindByEmailAsync(user).
All in all, you have to make sure the call to FindByEmailAsync(user) would indeed return the same user instance in your setup.
I believe what you want is:
signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(user, loginCommand.Password))
.ReturnsAsync((User user, string password) => { return result.Object; });
rather than Returns with a Task<T>. Other than that, the code calling the CheckPasswordSignInAsync method may be passing a different argument value than your Mock is configured with, such as a Hash for the password. You can set a breakpoint and inspect that the values passed in actually match what your test configured.
Normally with Mocks you would configure to expect It.IsAny and assert the values using means like:
signInServiceMock.Setup(l => l.CheckPasswordSignInAsync(It.IsAny<User>(), It.IsAny<string>()))
.ReturnsAsync((User user, string password) => { return result.Object; });
from here the Returns will received the passed in values if it needs to perform any simple computation to mock out, or you can verify, such as that the user and password passed matched via:
signInServiceMock.Verify(l => l.CheckPasswordSignInAsync(It.Is<User>(u => u.EMail == "testemail"), It.Is<string>(p => p == "testpassword"));
This would at least fail with some description that the method was called, but the expected parameters didn't match.

Unit test for API Controller in ASP.NET Core 3.1 returning a wrong status code

I'm writing a unit test for an API Controller performing delete action.
Here's the Delete Action
public IActionResult DeleteSubGenre(Guid subGenreId)
{
if (!_genreRepo.SubGenreExist(subGenreId))
{
return NotFound();
}
var genreObj = _genreRepo.SubGenre(subGenreId);
if (!_genreRepo.DeleteSubGenre(genreObj))
{
ModelState.AddModelError("", $"Something went wrong when deleting the record {genreObj.Name}");
return StatusCode(500, ModelState);
}
return NoContent();
}
The unit test for this action is written as
[Fact]
public void DeleteSubGenre_Returns_NoContentResult()
{
// Arrange
var subGenreRepositoryMock = new Mock<ISubGenreRepository>();
var subGenreIMapperMock = new MapperConfiguration(config =>
{
config.AddProfile(new MovieMapper());
});
var subGenreMapper = subGenreIMapperMock.CreateMapper();
SubGenresController subGenreApiController = new SubGenresController(subGenreRepositoryMock.Object, mapper: subGenreMapper);
var subGenreDto = new SubGenreDTO()
{
Name = "Adult Content",
DateCreated = DateTime.Parse("15 May 2015"),
Id = Guid.NewGuid(),
GenreId = Guid.NewGuid(),
Genres = new GenreDTO()
};
// Act
var subGenreResult = subGenreApiController.DeleteSubGenre(subGenreDto.Id);
var noContentResult = subGenreResult as NoContentResult;
// Assert
Assert.False(noContentResult.StatusCode is StatusCodes.Status204NoContent);
}
While debugging the test i noticed that subGenreResult was returning a status code of 404 instead of 204. I can seem to get a hang over it. I'll be glad to get plausible solution to this.
You have to setup your mock to drive the execution of your test case.
For example if you want to go through this line: if (!_genreRepo.SubGenreExist(subGenreId))
then you have to setup the following mock behaviour:
subGenreRepositoryMock.Setup(repo => repo.SubGenreExist(It.IsAny<Guid>)).Returns(true);
To reach this line: return NoContent(); you might need to setup the other two methods as well to drive your test case.

How to test a controller POST method which returns no data in response content in .NET Core 3.1?

i am new to integration tests. I have a controller method which adds a user to the database, as shown below:
[HttpPost]
public async Task<IActionResult> CreateUserAsync([FromBody] CreateUserRequest request)
{
try
{
var command = new CreateUserCommand
{
Login = request.Login,
Password = request.Password,
FirstName = request.FirstName,
LastName = request.LastName,
MailAddress = request.MailAddress,
TokenOwnerInformation = User
};
await CommandBus.SendAsync(command);
return Ok();
}
catch (Exception e)
{
await HandleExceptionAsync(e);
return StatusCode(StatusCodes.Status500InternalServerError,
new {e.Message});
}
}
As you have noticed my method returns no information about the user which has been added to the database - it informs about the results of handling a certain request using the status codes. I have written an integration test to check is it working properly:
[Fact]
public async Task ShouldCreateUser()
{
// Arrange
var createUserRequest = new CreateUserRequest
{
Login = "testowyLogin",
Password = "testoweHaslo",
FirstName = "Aleksander",
LastName = "Kowalski",
MailAddress = "akowalski#onet.poczta.pl"
};
var serializedCreateUserRequest = SerializeObject(createUserRequest);
// Act
var response = await HttpClient.PostAsync(ApiRoutes.CreateUserAsyncRoute,
serializedCreateUserRequest);
// Assert
response
.StatusCode
.Should()
.Be(HttpStatusCode.OK);
}
I am not sure is it enough to assert just a status code of response returned from the server. I am confused because, i don't know, shall i attach to assert section code, which would get all the users and check does it contain created user for example. I don't even have any id of such a user because my application finds a new id for the user while adding him/her to the database. I also have no idea how to test methods like that:
[HttpGet("{userId:int}")]
public async Task<IActionResult> GetUserAsync([FromRoute] int userId)
{
try
{
var query = new GetUserQuery
{
UserId = userId,
TokenOwnerInformation = User
};
var user = await QueryBus
.SendAsync<GetUserQuery, UserDto>(query);
var result = user is null
? (IActionResult) NotFound(new
{
Message = (string) _stringLocalizer[UserConstants.UserNotFoundMessageKey]
})
: Ok(user);
return result;
}
catch (Exception e)
{
await HandleExceptionAsync(e);
return StatusCode(StatusCodes.Status500InternalServerError,
new {e.Message});
}
}
I believe i should somehow create a user firstly in Arrange section, get it's id and then use it in Act section with the GetUserAsync method called with the request sent by HttpClient. Again the same problem - no information about user is returned, after creation (by the way - it is not returned, because of my CQRS design in whole application - commands return no information). Could you please explain me how to write such a tests properly? Have i missed anything? Thanks for any help.
This is how I do it:
var response = (CreatedResult) await _controller.Post(createUserRequest);
response.StatusCode.Should().Be(StatusCodes.Status201Created);
The second line above is not necessary, just there for illustration.
Also, your response it's better when you return a 201 (Created) instead of the 200(OK) on Post verbs, like:
return Created($"api/users/{user.id}", user);
To test NotFound's:
var result = (NotFoundObjectResult) await _controller.Get(id);
result.StatusCode.Should().Be(StatusCodes.Status404NotFound);
The NotFoundObjectResult assumes you are returning something. If you are just responding with a 404 and no explanation, replace NotFoundObjectResult with a NotFoundResult.
And finally InternalServerErrors:
var result = (ObjectResult) await _controller.Get(id);
result.StatusCode.Should().Be(StatusCodes.Status500InternalServerError);
You can use integrationFixture for that using this NuGet package. This is an AutoFixture alternative for integration tests.
The documented examples use Get calls but you can do other calls too. Logically, you should test for the status code (OkObjectResult means 200) value and the response (which could be an empty string, that is no problem at all).
Here is the documented example for a normal Get call.
[Fact]
public async Task GetTest()
{
// arrange
using (var fixture = new Fixture<Startup>())
{
using (var mockServer = fixture.FreezeServer("Google"))
{
SetupStableServer(mockServer, "Response");
var controller = fixture.Create<SearchEngineController>();
// act
var response = await controller.GetNumberOfCharacters("Hoi");
// assert
var request = mockServer.LogEntries.Select(a => a.RequestMessage).Single();
Assert.Contains("Hoi", request.RawQuery);
Assert.Equal(8, ((OkObjectResult)response.Result).Value);
}
}
}
private void SetupStableServer(FluentMockServer fluentMockServer, string response)
{
fluentMockServer.Given(Request.Create().UsingGet())
.RespondWith(Response.Create().WithBody(response, encoding: Encoding.UTF8)
.WithStatusCode(HttpStatusCode.OK));
}
In the example above, the controller is resolved using the DI described in your Startup class.
You can also do an actual REST call using using Refit. The application is self hosted inside your test.
using (var fixture = new RefitFixture<Startup, ISearchEngine>(RestService.For<ISearchEngine>))
{
using (var mockServer = fixture.FreezeServer("Google"))
{
SetupStableServer(mockServer, "Response");
var refitClient = fixture.GetRefitClient();
var response = await refitClient.GetNumberOfCharacters("Hoi");
await response.EnsureSuccessStatusCodeAsync();
var request = mockServer.LogEntries.Select(a => a.RequestMessage).Single();
Assert.Contains("Hoi", request.RawQuery);
}
}

How can I test methods which needs user to be logged

I'm testing some code which needs user to be logged in. When I'm trying to log in with AccountController, it's looks like everything is working, but at AccountController (IPrincipal) User is still null. How can I properly log in (or better, can I mock it somehow)?
public async Task SetupAsync()
{
var context = new DataContext();
var manager = new UserManager(new UserStore(context));
var accountController = new AccountController(manager);
var mockAuthenticationManager = new Mock<IAuthenticationManager>();
mockAuthenticationManager.Setup(am => am.SignOut());
mockAuthenticationManager.Setup(am => am.SignIn());
accountController.AuthenticationManager = mockAuthenticationManager.Object;
var user = new LoginViewModel
{
Email = "user#wp.pl",
Password = "useruser",
RememberMe = false
};
if (manager.FindByEmail("user#wp.pl") == null)
{
await manager.CreateAsync(new User { Email = "user#wp.pl", UserName = "user#wp.pl" }, "useruser");
}
await accountController.Login(user, "home/index");
_calendarController = new CalendarController(context);
}
Here I got User null exception:
public ClaimsPrincipal CurrentUser
{
get { return new ClaimsPrincipal((System.Security.Claims.ClaimsPrincipal)this.User); }
}
Edit: At return line, I have still User property null. This is sample from AccountController:
var user = await _userManager.FindAsync(model.Email, model.Password);
if (user != null)
{
await SignInAsync(user, model.RememberMe);
return RedirectToAction("index", "calendar");
}
You should mock your _userManager, and use a mock setup for when the method FindAsync is called. Then you return a fake user you can use later in the code
Figured it out on my own, probably not elegant solution but I'm happy anyway. #andreasnico your answer helped, thanks.
I'm mocking my custom ClaimsPrincipal, and setting up UserId - that's what I really needed.
var mockCp = new Mock<IClaimsPrincipal>();
mockCp.SetupGet(cp => cp.UserId).Returns(user.Id);
_calendarController.CurrentUser = mockCp.Object;

unwanted caching of db results or model

I have a controller action which i am performing some work on (example below)
public ActionResult Dashboard()
{
// Redirect back to login page if not authenticated
if (!this.HttpContext.User.Identity.IsAuthenticated)
{
return this.RedirectToAction(string.Empty, "Account");
}
this.ApplicationDbContext = new ApplicationDbContext();
this.UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(this.ApplicationDbContext));
var user = this.UserManager.FindById(this.User.Identity.GetUserId());
MyRitualDashboardData model = new MyRitualDashboardData();
Member member = db.Members.Single(m => m.AspNetUserId == user.Id);
model.Gravatar = string.Format("<img src='{0}' class='gravatar img-circle'/>", user.PhotoUrl);
model.UserMember = member;
model.UserHomeLocation = member.getUserHomeLocation();
model.UserActiveMembership = member.getActiveMembership();
model.UserPastMemberships = member.getExpiredMemberships();
model.UserPayments = member.getUserPayments();
model.UserStatistics = member.GetMembershipStatistics();
if (model.UserActiveMembership != null)
{
model.DaysTillMembershipExpiry = member.getActiveMembership().daysTillExpiry();
}
return this.View(model);
}
If i run this code and perform some changes to the database with some other actions then when i refresh my dashboard the various updates that i am making to the member profile is fine.
If however i try to re-factor my code and place the above code into a seperate class and use a static method to do the call like so:
public ActionResult Dashboard()
{
// Redirect back to login page if not authenticated
if (!this.HttpContext.User.Identity.IsAuthenticated)
{
return this.RedirectToAction(string.Empty, "Account");
}
this.ApplicationDbContext = new ApplicationDbContext();
this.UserManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(this.ApplicationDbContext));
var user = this.UserManager.FindById(this.User.Identity.GetUserId());
MyRitualDashboardData model = new MyRitualDashboardData();
model = MyRitualService.GetModelForMyRitual(user);
return this.View(model);
}
and have a method in a seperate class like so:
public static MyRitualDashboardData GetModelForMyRitual(ApplicationUser user)
{
MyRitualDashboardData model = new MyRitualDashboardData();
Member member = db.Members.Single(m => m.AspNetUserId == user.Id);
model.Gravatar = string.Format("<img src='{0}' class='gravatar img-circle'/>", user.PhotoUrl);
model.UserMember = member;
model.UserHomeLocation = member.getUserHomeLocation();
model.UserActiveMembership = member.getActiveMembership();
model.UserPastMemberships = member.getExpiredMemberships();
model.UserPayments = member.getUserPayments();
model.UserStatistics = member.GetMembershipStatistics();
if (model.UserActiveMembership != null)
{
model.DaysTillMembershipExpiry = member.getActiveMembership().daysTillExpiry();
}
return model;
}
Then it is using cached data from the database and i do not know why? Why would the same code called inline in the action be fine but when called from a seperate static function that it caches the data.
Any clues would be great.
Thanks
Both versions have the line
Member member = db.Members.Single(m => m.AspNetUserId == user.Id);
but since this is now in 2 different classes it must also be 2 different dbs.
The second one is static and will live longer (ie never refresh).
Avoid static. The separation in 2 classes is a good idea but just make instances.
Follow naming standards so that properties are easier to recognize.

Categories

Resources