26

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 = "ancon@mail.com", 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 = "ancon@mail.com", 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??

Nkosi
  • 191,971
  • 29
  • 311
  • 378
Anushka Madushan
  • 307
  • 1
  • 3
  • 10

3 Answers3

32

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", "user1@bv.com") { Id = 1 },
      new ApplicationUser("User2", "user2@bv.com") { 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", "New@test.com");
      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.

InteXX
  • 5,804
  • 5
  • 33
  • 54
djangojazz
  • 12,570
  • 9
  • 48
  • 82
31

aspnet/Identity is opensource so what you can do is see how they mock it themselves.

Here's how they do it: MockHelpers.cs

TestUserManager

public static UserManager<TUser> TestUserManager<TUser>(IUserStore<TUser> store = null) where TUser : class
{
    store = store ?? new Mock<IUserStore<TUser>>().Object;
    var options = new Mock<IOptions<IdentityOptions>>();
    var idOptions = new IdentityOptions();
    idOptions.Lockout.AllowedForNewUsers = false;
    options.Setup(o => o.Value).Returns(idOptions);
    var userValidators = new List<IUserValidator<TUser>>();
    var validator = new Mock<IUserValidator<TUser>>();
    userValidators.Add(validator.Object);
    var pwdValidators = new List<PasswordValidator<TUser>>();
    pwdValidators.Add(new PasswordValidator<TUser>());
    var userManager = new UserManager<TUser>(store, options.Object, new PasswordHasher<TUser>(),
        userValidators, pwdValidators, new UpperInvariantLookupNormalizer(),
        new IdentityErrorDescriber(), null,
        new Mock<ILogger<UserManager<TUser>>>().Object);
    validator.Setup(v => v.ValidateAsync(userManager, It.IsAny<TUser>()))
        .Returns(Task.FromResult(IdentityResult.Success)).Verifiable();
    return userManager;
}
Jess
  • 20,424
  • 18
  • 108
  • 130
Nick Chapsas
  • 5,749
  • 1
  • 11
  • 25
  • It would be great if you added a simple code sample based the class you link to. This will make the answer more useful to future users since links can break over time. – Sixto Saez Mar 08 '18 at 15:15
  • @SixtoSaez Thanks for the suggestion, i added the code snippet. – Nick Chapsas Mar 08 '18 at 15:45
  • I just came across this exact same issue. Wow, all that to mock user manager? Wonder why there is no interface for UserManager? The store seems to have one (IUserStore<>), etc. – Todd Langdon Aug 08 '18 at 18:49
  • If you're needing to use a DbContext in your tests (I use the InMemory DbContext implementation), you can pass that in and create a real instance of UserStore rather than a Mock, e.g. var store = new UserStore(yourDbContextInstance); – Sam Aug 22 '18 at 18:49
  • I was hoping I could just call this method, but it is not part of the library. – Jess Aug 30 '19 at 12:38
  • Thanks for your answer @NickChapsas, it is really useful. However I had the hard time to figure out how to use it. I couldn't solve the mocking with your linked `TestUserManager`, it throw an exception when it was called. However my solution was to use the `MockUserManager` frome the same source you linked. Do you think is it a correct application? – Milán Pataki May 28 '21 at 07:38
3

In .NetCore 2.2 you have to do it slightly different. Treat it like an update to @Nick Chapsas answer.

First of all, you have to use IUserPasswordStore instead of IUserStore. IUserPasswordStore inherits IUserStore, but UserManager would like to get IUserPasswordStore. In other way, some things won't work.

If you want to test real behaviour of UserManager (for example CreateUserAsync), you can use real implementations of UserValidator and PasswordValidator. You may want to just to be sure that your method reacts how it supposed to for CreateUser errors.

This is my updated example:

UserManager<TUser> CreateUserManager() where TUser : class
{
    Mock<IUserPasswordStore<TUser>> userPasswordStore = new Mock<IUserPasswordStore<TUser>>();
    userPasswordStore.Setup(s => s.CreateAsync(It.IsAny<TUser>(), It.IsAny<CancellationToken>()))
        .Returns(Task.FromResult(IdentityResult.Success));

    var options = new Mock<IOptions<IdentityOptions>>();
    var idOptions = new IdentityOptions();

    //this should be keep in sync with settings in ConfigureIdentity in WebApi -> Startup.cs
    idOptions.Lockout.AllowedForNewUsers = false;
    idOptions.Password.RequireDigit = true;
    idOptions.Password.RequireLowercase = true;
    idOptions.Password.RequireNonAlphanumeric = true;
    idOptions.Password.RequireUppercase = true;
    idOptions.Password.RequiredLength = 8;
    idOptions.Password.RequiredUniqueChars = 1;

    idOptions.SignIn.RequireConfirmedEmail = false;

    // Lockout settings.
    idOptions.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
    idOptions.Lockout.MaxFailedAccessAttempts = 5;
    idOptions.Lockout.AllowedForNewUsers = true;


    options.Setup(o => o.Value).Returns(idOptions);
    var userValidators = new List<IUserValidator<TUser>>();
    UserValidator<TUser> validator = new UserValidator<TUser>();
    userValidators.Add(validator);

    var passValidator = new PasswordValidator<TUser>();
    var pwdValidators = new List<IPasswordValidator<TUser>>();
    pwdValidators.Add(passValidator);
    var userManager = new UserManager<TUser>(userPasswordStore.Object, options.Object, new PasswordHasher<TUser>(),
        userValidators, pwdValidators, new UpperInvariantLookupNormalizer(),
        new IdentityErrorDescriber(), null,
        new Mock<ILogger<UserManager<TUser>>>().Object);

    return userManager;
}

Notice that UserPasswordStore has a method (CreateAsync) that should be mocked if you want to test CreateAsync from UserManager.

Password and Lockout settings are taken from my project. They should be kept in sync with your settings, so that you can test the real thing.

Of course you don't test for example PasswordValidator, but you can test your methods, for example:

//Part of user service
public async Task<IdentityResult> Register(UserDto data)
{
    SystemUser user = ConvertDtoToUser(data);
    IdentityResult result = userManager.CreateAsync(user, data.Password);

    //some more code that is dependent on the result
}
Adam Jachocki
  • 1,494
  • 8
  • 19