Is there any way to Mock UserManager with all stores?

Viewed 32

I want to write some unit tests for my authentication handler in web API, but I have some problems because I use Identity to build my db tables in easy way. So I use some UserManager methods in my authentication handlers, such as FindByEmaiAsync/FindByUserNameAsync/CreateAsync and CheckPasswordSignInAsync method of SignInManager. So when it comes to unit testing, I found out that it's quite difficult to mock UserManager for example, because you need to use only one store, for example, if you pass IUserStore as store, in result you get error because there is no IUserEmailStore, when you use IUserEmailStore, you get another error that tells that there is no IUserPasswordStore. So is there any way to get UserManager with all stores that I need in my Unit test? There is my code

public class CreateUserCommandHandler : IRequestHandler<CreateUserCommand, AuthResult>
{
    private readonly UserManager<AppUser> _userManager;
    private readonly IAuthService _authService;

    public CreateUserCommandHandler(UserManager<AppUser> userManager, IAuthService authService)
        => (_userManager, _authService) = (userManager, authService);

    public async Task<AuthResult> Handle(CreateUserCommand request, CancellationToken cancellationToken)
    {
        var userByEmail = await _userManager.FindByEmailAsync(request.Email);
        var userByName = await _userManager.FindByNameAsync(request.UserName);

        if(userByEmail is not null || userByName is not null)
        {
            throw new Exception($"User with email {request.Email} or name {request.UserName} is already exists");
        }

        var user = new AppUser
        {
            UserName = request.UserName,
            Email = request.Email
        };

        var token = _authService.GenerateJwtToken(user);

        var result = await _userManager.CreateAsync(user, request.Password);

        if (!result.Succeeded)
        {
            return new AuthResult
            {
                Success = false,
                Error = "Server error occured"
            };
        }

        return new AuthResult
        {
            Success = true,
            Token = token,
            UserId = user.Id
        };
    }
}

There is AuthService:

public class JwtAuthService : IAuthService
{
    private readonly JwtSettings _settings;

    public JwtAuthService(IConfiguration configuration)
    {
        _settings = new JwtSettings();
        configuration.Bind("JwtSettings", _settings);
    }

    public string GenerateJwtToken(AppUser user)
    {
        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_settings.Key));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var token = new JwtSecurityToken(_settings.Issuer,
            _settings.Issuer,
            new List<Claim>
            {
                new (ClaimTypes.Name, user.UserName),
                new (ClaimTypes.Email, user.Email),
                new (JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            },
            expires: DateTime.Now.AddMinutes(20),
            signingCredentials: credentials);

        return new JwtSecurityTokenHandler().WriteToken(token);

    }
}

Unit test for this handler that fails because of stores:

public class CreateUserCommandHandlerTests : TestCommandBase
{
    private readonly FakeJwtConfiguration _configuration;

    public CreateUserCommandHandlerTests()
    {
        _configuration = new FakeJwtConfiguration
        {
            Key = "W2DykK0cEkPAa8jqOkTamsvVIRRjbJmAM3ZbcgR5",
            Issuer = "https://localhost:7083"
        };
    }

    [Fact]
    public async Task CreateUserCommandHandlerTests_Succes()
    {
        //Arrange
        var userName = "TestUser";
        var password = "TestUser123.";
        var userEmail = "email@gmail.com";

        var userManager = MockHelper.TestUserManager<AppUser>();
        

        var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration.Key));
        var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);

        var mockAuthService = new Mock<IAuthService>();
        mockAuthService.Setup(m => m.GenerateJwtToken(It.IsAny<AppUser>())).
            Returns(new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(_configuration.Issuer,
            _configuration.Issuer,
            new List<Claim>
            {
                new (ClaimTypes.Name, userName),
                new (ClaimTypes.Email, userEmail),
                new (JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
            },
            expires: DateTime.Now.AddMinutes(20),
            signingCredentials: credentials)));

        var handler = new CreateUserCommandHandler(userManager, mockAuthService.Object);

        //Act
        var result = await handler.Handle(new CreateUserCommand
        {
            Email = userEmail,
            UserName = userName,
            Password = password
        },
        CancellationToken.None);

        //Assers
        result.ShouldBeOfType<AuthResult>();
        result.Success.ShouldBe(true);
        result.Token.ShouldNotBeEmpty();

    }
}

And MockHelper class that returns me mocked UserManager:

 public static class MockHelper
{
    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 emailStore = new Mock<IUserEmailStore<TUser>>().Object;
        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;
    }
}
0 Answers
Related