在ASP.NET Core中对自定义密码验证器进行单元测试

问题描述:

我有一个CustomPasswordValidator.cs文件,它覆盖了PasswordValidator

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

我是不熟悉Moq和xUnit的新手.我正在尝试创建一个单元测试,以确保产生正确数量的错误(显示工作代码,以及在注释中产生错误的代码):

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

IdentityResult行上发生错误,表明我无法将Mock转换为UserManager,也无法将Mock转换为AppUser类.

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

已更改为包括在ASP.NET核心中模拟UserManagerClass所需的GetMockUserManager()(

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

使用Moq,您需要在模拟对象上调用.Object以获得模拟对象.您还应该使测试异步,然后等待被测方法.

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.

您还在嘲笑被测对象,在这种情况下,这将导致被测方法在调用时返回null,因为它没有正确设置.此时,您基本上正在测试模拟框架.

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.

创建被测对象的实际实例CustomPasswordValidator并进行测试,模拟被测对象的显式依赖关系以获得所需的行为.

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

}

阅读 Moq快速入门,以更加熟悉如何使用moq.

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