Reputation: 2009
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
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