Unit Testing Custom Password Validators in ASP.NET

2020-03-24 06:33发布

I have a CustomPasswordValidator.cs file that overrides PasswordValidator

 public class CustomPasswordValidator : PasswordValidator<AppUser>
    {   //override the PasswordValidator functionality with the custom definitions
        public override async Task<IdentityResult> ValidateAsync(UserManager<AppUser> manager, AppUser user, string password)
        {
            IdentityResult result = await base.ValidateAsync(manager, user, password);

            List<IdentityError> errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList();

            //check that the username is not in the password
            if (password.ToLower().Contains(user.UserName.ToLower()))
            {
                errors.Add(new IdentityError
                {
                    Code = "PasswordContainsUserName",
                    Description = "Password cannot contain username"
                });
            }

            //check that the password doesn't contain '12345'
            if (password.Contains("12345"))
            {
                errors.Add(new IdentityError
                {
                    Code = "PasswordContainsSequence",
                    Description = "Password cannot contain numeric sequence"
                });
            }
            //return Task.FromResult(errors.Count == 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray()));
            return errors.Count == 0 ? IdentityResult.Success : IdentityResult.Failed(errors.ToArray());
        }
    }

I am new to using Moq and xUnit. I am trying to create a unit test to ensure that the correct number of errors are produced (showing working code, with code that produced errors in comments):

//test the ability to validate new passwords with Infrastructure/CustomPasswordValidator.cs 
        [Fact]
        public async void Validate_Password()
        {
            //Arrange
            <Mock><UserManager<AppUser>> userManager = new <Mock><UserManager<AppUser>>(); //caused null exception, use GetMockUserManager() instead
            <Mock><CustomPasswordValidator> customVal = new <Mock><CustomPasswordValidator>(); //caused null result object use customVal = new <CustomPasswordValidator>() instead
            <AppUser> user = new <AppUser>
            user.Name = "user" 
            //set the test password to get flagged by the custom validator
            string testPwd = "Thi$user12345";

            //Act
            //try to validate the user password
            IdentityResult result = await customVal.ValidateAsync(userManager, user, testPwd);

            //Assert
            //demonstrate that there are two errors present
            List<IdentityError> errors = result.Succeeded ? new List<IdentityError>() : result.Errors.ToList();
            Assert.Equal(errors.Count, 2);
        }

//create a mock UserManager class
        private Mock<UserManager<AppUser>> GetMockUserManager()
        {
            var userStoreMock = new Mock<IUserStore<AppUser>>();
            return new Mock<UserManager<AppUser>>(
                userStoreMock.Object, null, null, null, null, null, null, null, null);
        }

The error occurs on the IdentityResult line, indicating that I cannot convert a Mock to UserManager and cannot convert Mock to AppUser class.

EDIT: changed to include GetMockUserManager() needed to mock a UserManagerClass in ASP.NET core (Mocking new Microsoft Entity Framework Identity UserManager and RoleManager)

1条回答
smile是对你的礼貌
2楼-- · 2020-03-24 07:16

With Moq you need call .Object on the mock to get the mocked object. You should also make the test async and await the method under test.

You are also mocking the subject under test which in this case is cause the method under test to return null when called because it would not have been setup appropriately. You are basically testing the mocking framework at that point.

Create an actual instance of the subject under test CustomPasswordValidator and exercise the test, mocking the explicit dependencies of the subject under test to get the desired behavior.

public async Task Validate_Password() {

    //Arrange
    var userManagerMock = new GetMockUserManager();
    var subjetUnderTest = new CustomPasswordValidator();
    var user = new AppUser() {
        Name = "user" 
    }; 
    //set the test password to get flagged by the custom validator
    var password = "Thi$user12345";

    //Act
    IdentityResult result = await subjetUnderTest.ValidateAsync(userManagerMock.Object, user, password);


    //...code removed for brevity

}

Read Moq Quickstart to get more familiar with how to use moq.

查看更多
登录 后发表回答