Joelty
Joelty

Reputation: 2009

Creating an instance of UserManager<T> in tests

I'm very new to the concept of mocking and I struggle with injecting/mocking an UserManager<T> to my tests

I need to use await _userManager.CreateAsync(user, password);

I've found this one

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;
}

But this throws

System.NotSupportedException : Store does not implement IUserPasswordStore.

How can I create an working UserManager<T> instance in tests?

I'm using Xunit

Upvotes: 1

Views: 555

Answers (1)

Amirhossein Ghorbani
Amirhossein Ghorbani

Reputation: 751

First of all revise about your approach, I've read some different papers that people says you don't need to test a library and a tool that has been already tested. But sometimes we need to mock UserManager to inject our services and resolve these dependencies. UserManager can be mocked really simple, but error messages like that are not well explained. We must mock the UserStore first and give it to UserManager, to mock UserStore you can create a class MockUserStore which derives IUserStore and IUserPasswordStore :

public class MockUserStore : IUserStore<TUser>, IUserPasswordStore<TUser>

This mock class must implement some methods of IUserPasswordStore, otherwise you catch that error message!

public Task SetPasswordHashAsync(TUser user, string passwordHash,
     CancellationToken cancellationToken)
{
    user.PasswordHash = passwordHash;
    return Task.FromResult(0);
}

public Task<string> GetPasswordHashAsync(TUser user, CancellationToken cancellationToken)
{
    return Task.FromResult<string>(user.PasswordHash);
}

public Task<bool> HasPasswordAsync(TUser user, CancellationToken cancellationToken)
{
    return Task.FromResult<bool>(!String.IsNullOrEmpty(user.PasswordHash));
}

You can also use a class to mock UserManager and override its methods with mocked methods:

public class MockUserManager : UserManager<TUser>
{
    public MockUserManager(IUserStore<TUser> store, IOptions<IdentityOptions> optionsAccessor,
     IPasswordHasher<TUser> passwordHasher, IEnumerable<IUserValidator<TUser>> userValidators,
      IEnumerable<IPasswordValidator<TUser>> passwordValidators, ILookupNormalizer keyNormalizer,
       IdentityErrorDescriber errors, IServiceProvider services, ILogger<UserManager<TUser>> logger)
        : base(store, optionsAccessor, passwordHasher, userValidators, passwordValidators, keyNormalizer, errors, services, logger)
    {
    }

    public override Task<IdentityResult> CreateAsync(TUser user)
    {
        return Task.FromResult(IdentityResult.Success);
    }
}

after all you can mock the UserManager with passing mocked UserStore:

    var userStore = new MockUserStore();
    var userManager = new MockUserManager(userStore,
                        new Mock<IOptions<IdentityOptions>>().Object,
                        new Mock<IPasswordHasher<AppUser>>().Object,
                        new IUserValidator<TUser>[0],
                        new IPasswordValidator<TUser>[0],
                        new Mock<ILookupNormalizer>().Object,
                        new Mock<IdentityErrorDescriber>().Object,
                        new Mock<IServiceProvider>().Object,
                        new Mock<ILogger<UserManager<TUser>>>().Object);
    return userManager;

Upvotes: 2

Related Questions