Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to mock UserManager in .Net Core testing?

I have following code. Im trying to running a test case for create user.Following is what i have tried so far.

public class CreateUserCommandHandlerTest {     private Mock<UserManager<ApplicationUser>> _userManager;     private CreateUserCommandHandler _systemUnderTest;      public CreateUserCommandHandlerTest()     {         _userManager = MockUserManager.GetUserManager<ApplicationUser>();         var user = new ApplicationUser() { UserName = "ancon1", Email = "[email protected]", RoleType = RoleTypes.Anonymous };         _userManager             .Setup(u => u.CreateAsync(user, "ancon2")).ReturnsAsync(IdentityResult.Success);         _systemUnderTest = new CreateUserCommandHandler(_userManager.Object);     }      [Fact]     public async void Handle_GivenValidInput_ReturnsCreatedResponse()     {         var command = new CreateUserCommand { Username = "ancon1", Email = "[email protected]", Password = "ancon2", RoleType = RoleTypes.Anonymous };         var result = await _systemUnderTest.Handle(command, default(CancellationToken));         Assert.NotNull(result);         Assert.IsType<Application.Commands.CreatedResponse>(result);     } } 

My User manager is here:

public static class MockUserManager {     public static Mock<UserManager<TUser>> GetUserManager<TUser>()         where TUser : class     {         var store = new Mock<IUserStore<TUser>>();         var passwordHasher = new Mock<IPasswordHasher<TUser>>();         IList<IUserValidator<TUser>> userValidators = new List<IUserValidator<TUser>>         {             new UserValidator<TUser>()         };         IList<IPasswordValidator<TUser>> passwordValidators = new List<IPasswordValidator<TUser>>         {             new PasswordValidator<TUser>()         };         userValidators.Add(new UserValidator<TUser>());         passwordValidators.Add(new PasswordValidator<TUser>());         var userManager = new Mock<UserManager<TUser>>(store.Object, null, passwordHasher.Object, userValidators, passwordValidators, null, null, null, null);         return userManager;     } } 

and my Command handler is this:

 public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, BaseCommandResponse> {     private readonly UserManager<ApplicationUser> _userManager;      public CreateUserCommandHandler(UserManager<ApplicationUser> userManager)     {         _userManager = userManager;     }      public async Task<BaseCommandResponse> Handle(CreateUserCommand createUserCommand, CancellationToken cancellationToken)     {         var user = new ApplicationUser { UserName = createUserCommand.Username, Email = createUserCommand.Email, RoleType = createUserCommand.RoleType };         var result = await _userManager.CreateAsync(user, createUserCommand.Password);         if (result.Succeeded)         {             return new CreatedResponse();         }          ErrorResponse errorResponse = new ErrorResponse(result.Errors.Select(e => e.Description).First());          return errorResponse;     } } 

when i'm running my test it fails and saying Object reference not set to an instant of an object.

What am i doing wrong here??

like image 236
Anushka Madushan Avatar asked Mar 08 '18 05:03

Anushka Madushan


1 Answers

I know this is months old but I keep getting back to this thread. I will extend my own answer on this topic because just pointing to Haok's GitHub example is like saying: "Read a book" as it is huge. It does not pinpoint the issue and what you need to do. You need to isolate a Mock object, but not only that but also you need to 'Setup' the method for 'CreateAsync'. So let's put this in three parts:

  1. You need to MOCK if you are using MOQ or a similar framework to make a mocked up creation of the UserManager.
  2. You need to Setup the methods of UserManager you expect to get results back from.
  3. Optionally you would want to inject some generic list from a mocked Entity Framework Core 2.1 or similar so that you can actually see that a list of IDentity Users actually increases or decreases. Not just that UserManager succeeded and nothing else

So say I have a helper method for returning a Mocked UserManager. Which is just slightly altered from the Haok code:

public static Mock<UserManager<TUser>> MockUserManager<TUser>(List<TUser> ls) where TUser : class {     var store = new Mock<IUserStore<TUser>>();     var mgr = new Mock<UserManager<TUser>>(store.Object, null, null, null, null, null, null, null, null);     mgr.Object.UserValidators.Add(new UserValidator<TUser>());     mgr.Object.PasswordValidators.Add(new PasswordValidator<TUser>());      mgr.Setup(x => x.DeleteAsync(It.IsAny<TUser>())).ReturnsAsync(IdentityResult.Success);     mgr.Setup(x => x.CreateAsync(It.IsAny<TUser>(), It.IsAny<string>())).ReturnsAsync(IdentityResult.Success).Callback<TUser, string>((x, y) => ls.Add(x));     mgr.Setup(x => x.UpdateAsync(It.IsAny<TUser>())).ReturnsAsync(IdentityResult.Success);      return mgr; } 

What is key to this is I am injecting a generic 'TUser' that is what I will be testing as well injecting a list of this. Similar to my example of:

 private List<ApplicationUser> _users = new List<ApplicationUser>  {       new ApplicationUser("User1", "[email protected]") { Id = 1 },       new ApplicationUser("User2", "[email protected]") { Id = 2 }  };       ...   private _userManager = MockUserManager<ApplicationUser>(_users).Object;  

Then finally I am testing a pattern with a repository similar to this implementation I want to test:

 public async Task<int> CreateUser(ApplicationUser user, string password) => (await _userManager.CreateAsync(user, password)).Succeeded ? user.Id : -1; 

I test it like this:

 [Fact]  public async Task CreateAUser()  {       var newUser = new ApplicationUser("NewUser", "[email protected]");       var password = "P@ssw0rd!";        var result = await CreateUser(newUser, password);        Assert.Equal(3, _users.Count);   } 

The key to what I did is that not only did I 'Setup' the CreateAsync but I provided a callback so I can actually see my list I inject get incremented. Hope this helps someone.

like image 143
djangojazz Avatar answered Sep 25 '22 17:09

djangojazz