模拟SignInManager

时间:2018-01-10 14:35:14

标签: c# asp.net unit-testing asp.net-core moq

使用Moq和xUnit进行单元测试的新手。我试图模拟控制器构造函数中使用的SignInManager来构建单元测试。我可以为SignInManager构造函数找到的文档说它接受UserManagerAuthenticationManager对象:https://msdn.microsoft.com/en-us/library/mt173769(v=vs.108).aspx#M:Microsoft.AspNet.Identity.Owin.SignInManager`2。

当我尝试模拟控制器时,我收到错误消息,说它无法实例化SignInManagerAuthenticationManager类的代理。

错误:

  

"消息:   Castle.DynamicProxy.InvalidProxyConstructorArgumentsException:可以   没有实例化类的代理:   Microsoft.AspNetCore.Identity.SignInManager1 [Models.AppUser,   ,版本= 1.0.0.0,文化=中立,   公钥=空]。找不到匹配的构造函数   给定参数:Castle.Proxies.UserManager`1Proxy   Castle.Proxies.AuthenticationManagerProxy"

单元测试:

public void Can_Send_Password_Reset_Email()
{
    //Arrange
    //create mock services
    Mock<IEmailService> mockEmailService = new Mock<IEmailService>();
    Mock<ILessonRepository> mockRepo = new Mock<ILessonRepository>();
    Mock<UserManager<AppUser>> mockUsrMgr = GetMockUserManager();
    var mockSignInMgr = GetMockSignInManager();
    Mock<UserValidator<AppUser>> mockUsrVal = new Mock<UserValidator<AppUser>>();
    Mock<PasswordValidator<AppUser>> mockPwdVal = new Mock<PasswordValidator<AppUser>>();
    Mock<PasswordHasher<AppUser>> mockPwdHshr = new Mock<PasswordHasher<AppUser>>();
    Mock<ForgotPasswordModel> model = new Mock<ForgotPasswordModel>();
    model.Object.Email = "joe@example.com";

    var user = new AppUser();
    var token = mockUsrMgr.Object.GeneratePasswordResetTokenAsync(user).Result;

    //create mock temporary data, needed for controller message
    Mock<ITempDataDictionary> tempData = new Mock<ITempDataDictionary>();
    //create the controller
    //ERROR ON THIS LINE
    AccountController controller = new AccountController(mockUsrMgr.Object, mockSignInMgr.Object, mockUsrVal.Object, mockPwdVal.Object, mockPwdHshr.Object, mockEmailService.Object)
    {
        TempData = tempData.Object
    };

    //Act
    //the controller should call the email action method
    controller.PasswordResetEmail(model.Object);

    //Assert
    //verify that the email service method was called one time
    mockEmailService.Verify(m => m.PasswordResetMessage(user, "Test Email"), Times.Once());
}

SignInManager模拟功能:

//create a mock SignInManager class
private Mock<SignInManager<AppUser>> GetMockSignInManager()
{
    var mockUsrMgr = GetMockUserManager();
    var mockAuthMgr = new Mock<AuthenticationManager>();
    return new Mock<SignInManager<AppUser>>(mockUsrMgr.Object, mockAuthMgr.Object);
}

GetMockUserManager()在其他单元测试中运行良好,但似乎不是问题。

5 个答案:

答案 0 :(得分:7)

这就是我的方法,希望对您有帮助。

public class FakeSignInManager : SignInManager<ApplicationUser>
{
    public FakeSignInManager()
            : base(new Mock<FakeUserManager>().Object,
                 new Mock<IHttpContextAccessor>().Object,
                 new Mock<IUserClaimsPrincipalFactory<ApplicationUser>>().Object,
                 new Mock<IOptions<IdentityOptions>>().Object,
                 new Mock<ILogger<SignInManager<ApplicationUser>>>().Object,
                 new Mock<IAuthenticationSchemeProvider>().Object)
        { }        
}



public class FakeUserManager : UserManager<ApplicationUser>
    {
        public FakeUserManager()
            : base(new Mock<IUserStore<ApplicationUser>>().Object,
              new Mock<IOptions<IdentityOptions>>().Object,
              new Mock<IPasswordHasher<ApplicationUser>>().Object,
              new IUserValidator<ApplicationUser>[0],
              new IPasswordValidator<ApplicationUser>[0],
              new Mock<ILookupNormalizer>().Object,
              new Mock<IdentityErrorDescriber>().Object,
              new Mock<IServiceProvider>().Object,
              new Mock<ILogger<UserManager<ApplicationUser>>>().Object)
        { }

        public override Task<IdentityResult> CreateAsync(ApplicationUser user, string password)
        {
            return Task.FromResult(IdentityResult.Success);
        }

        public override Task<IdentityResult> AddToRoleAsync(ApplicationUser user, string role)
        {
            return Task.FromResult(IdentityResult.Success);
        }

        public override Task<string> GenerateEmailConfirmationTokenAsync(ApplicationUser user)
        {
            return Task.FromResult(Guid.NewGuid().ToString());
        }

    }

答案 1 :(得分:3)

您正在查看的文档适用于Microsoft.AspNet.Identity.Owin.SignInManager

如果查看错误消息,您会看到Moq实际上正在尝试创建Microsoft.AspNetCore.Identity.SignInManager类型的对象。请注意AspNetAspNetCore之间的区别。

基本上,您正在查看经典ASP.NET中旧的基于Owin的Identity堆栈中使用的SignInManager。但是您需要查看ASP.NET Core版本。如果您查看该类型的文档,您会看到constructor has a few more dependencies

public SignInManager(
    UserManager<TUser> userManager,
    IHttpContextAccessor contextAccessor,
    IUserClaimsPrincipalFactory<TUser> claimsFactory,
    IOptions<Microsoft.AspNetCore.Identity.IdentityOptions> optionsAccessor,
    ILogger<Microsoft.AspNetCore.Identity.SignInManager<TUser>> logger,
    IAuthenticationSchemeProvider schemes);

答案 2 :(得分:1)

对于ASP.NET Core 5,可以使用以下设置:

var userManagerMock = new Mock<UserManager<User>>(
    /* IUserStore<TUser> store */Mock.Of<IUserStore<User>>(),
    /* IOptions<IdentityOptions> optionsAccessor */null,
    /* IPasswordHasher<TUser> passwordHasher */null,
    /* IEnumerable<IUserValidator<TUser>> userValidators */null,
    /* IEnumerable<IPasswordValidator<TUser>> passwordValidators */null,
    /* ILookupNormalizer keyNormalizer */null,
    /* IdentityErrorDescriber errors */null,
    /* IServiceProvider services */null,
    /* ILogger<UserManager<TUser>> logger */null);

var signInManagerMock = new Mock<SignInManager<User>>(
    userManagerMock.Object,
    /* IHttpContextAccessor contextAccessor */Mock.Of<IHttpContextAccessor>(),
    /* IUserClaimsPrincipalFactory<TUser> claimsFactory */Mock.Of<IUserClaimsPrincipalFactory<User>>(),
    /* IOptions<IdentityOptions> optionsAccessor */null,
    /* ILogger<SignInManager<TUser>> logger */null,
    /* IAuthenticationSchemeProvider schemes */null,
    /* IUserConfirmation<TUser> confirmation */null);

答案 3 :(得分:0)

SignInManager没有构造函数,没有参数,因此您必须在new Mock<SignInManager>(**Params here**)中传递参数。

这些参数中的某些不能不为null,因此必须像这样嘲笑它们:

  1. UserManager<TUser> ==>第一个参数。
  2. IHttpContextAccessor ==>第二个参数。

这里的问题甚至是UserManager也不缺少参数构造函数,因此您必须模拟其参数

对于Usermanager,不应为空的参数IUserStore<User> ==>第一个参数

这就是我如何用最少的代码完成

var _mockUserManager = new Mock<ApiUserManager>(new Mock<IUserStore<ApiUser>>().Object,
                null, null, null, null, null, null, null, null);
var _contextAccessor = new Mock<IHttpContextAccessor>();
var _userPrincipalFactory = new Mock<IUserClaimsPrincipalFactory<ApiUser>>();
Mock<ApiSignInManager>mockApiSignInManager = new Mock<ApiSignInManager>(_mockUserManager.Object,
               _contextAccessor.Object, _userPrincipalFactory.Object, null, null, null);

此回复受到umutesen on GitHub Forum

的启发

答案 4 :(得分:0)

我在 ASP.NET Core 5 中通过 FakeIsEasy 直接伪造 let query = productsRef; filters.map((filter) => { query = query.where(filter.key, filter.operation, filter.value); });

SignInManager<User>