我试图模拟在.net core中使用Identity生成电子邮件令牌

本文关键字:Identity 令牌 电子邮件 模拟 core net | 更新日期: 2023-09-27 18:11:39

我正试图在。net核心中做一些嘲弄,特别是围绕身份。我已经创建了一个。net核心类库项目,我有信心我有所有正确的参考。我试图模拟的第一件事是身份方法GenerateConfirmationTokenAsync方法,因为我试图模拟注册用户的设置。这是目前为止我在我的测试类中使用moq和Xunit的内容。

public class ServiceTests
{
    private readonly string _email;
    private readonly string _subject;
    private readonly string _message;
    private readonly Mock<IAccountService> _accountService;
    private readonly ApplicationUser _applicationUser;
    private readonly Mock<VisualJobsDbContext> _identityDbContext;
    public ServiceTests()
    {
        _email = "me@gmail.com";
        _subject = "test from me";
        _message = "hello I got to you";
        _accountService = new Mock<IAccountService>();
        _applicationUser = new ApplicationUser { UserName = _email, Email = _email };
        _identityDbContext = new Mock<VisualJobsDbContext>();
    }
    [Fact]
    public async Task GenerateConfirmationToken()
    {
        Mock<DbSet<ApplicationUser>> userMock = DbSetMock.Create(_applicationUser);
        var register = await _accountService.Object.Register(userMock.Object.First(), "password");
        var token = await _accountService.Object.GenerateEmailConfirmationTokenAsync(userMock.Object.First());
        Assert.NotNull(token);
    }

我的ApplicationUser类继承了IdentityUser 'register'和'token'总是空的。如果我查看userMock,我也看不到一个令牌

我试图模拟在.net core中使用Identity生成电子邮件令牌

为了测试模拟,首先需要设置它做一些事情,否则它只是一个没有实现的空接口。

在您的特定情况下,似乎您根本不需要mock。创建ApplicationUser用户的实例,从中创建DbSet模拟,然后从中取出第一个对象。这与直接将ApplicationUser实例传递给您的服务相同。

相反,您正在将ApplicationUser传递给mock,它根本没有实现,因此实际上您根本没有测试任何内容。

public class ServiceTests
{
    private readonly string _email;
    private readonly string _subject;
    private readonly string _message;
    private readonly Mock<IAccountService> _accountService;
    private readonly ApplicationUser _applicationUser;
    private readonly Mock<VisualJobsDbContext> _identityDbContext;
    public ServiceTests()
    {
        _email = "me@gmail.com";
        _subject = "test from me";
        _message = "hello I got to you";
        _applicationUser = new ApplicationUser { UserName = _email, Email = _email };
    }
    [Fact]
    public async Task GenerateConfirmationToken()
    {
        // Does it have dependencies? If yes, you may need to mock them
        var _accountService = new AccountService(.../*mocked dependencies*/);
        var register = await _accountService.Register(_applicationUser, "password");
        var token = await _accountService.GenerateEmailConfirmationTokenAsync(userMock.Object.First());
        Assert.NotNull(token);
    }

现在,这取决于你在RegisterGenerateEmailConfirmationTokenAsync中有什么样的逻辑,如果你需要moq,你在AccountService中有哪些依赖关系。

让我们假设你有一个名为TokenGenerator的服务,它实现了ITokenGenerator接口。

public class ServiceTests
{
    private readonly string _email;
    private readonly string _subject;
    private readonly string _message;
    private readonly ApplicationUser _applicationUser;
    private readonly Mock<ITokenGenerator> _tokenGenerator;
    public ServiceTests()
    {
        _email = "me@gmail.com";
        _subject = "test from me";
        _message = "hello I got to you";
        _applicationUser = new ApplicationUser { UserName = _email, Email = _email };
        _tokenGenerator = Mock<ITokenGenerator>();
    }
    [Fact]
    public async Task GenerateConfirmationToken()
    {
        // #### Setup ####
        // Reads: If GenerateToken method is called with the **exact** same instance as the user passed to the service
        _tokenGenerator.Setup(t => t.GenerateToken(It.Is(user)))
            // then return "abc123456" as token
            .Returns("abcd123456")
            // Verify that the method is called with the exact conditions from above, otherwise fail
            // i.e. if GenerateToken is called with a different instance of user, test will fail
            .Verifiable("ContainsKey not called.");
        // #### ACT ####
        // Pass the token generator mock to our account service
        var _accountService = new AccountService(_tokenGenerator.Object);
        var register = await _accountService.Register(_applicationUser, "password");
        var token = await _accountService.GenerateEmailConfirmationTokenAsync(userMock.Object.First());
        // #### VERIFY ####
        // Verify that GenerateToken method has been called with correct parameters
        _tokenGenerator.Verify();
        // verify that the GenerateEmailConfirmationTokenAsync returned the expected token abc123456
        Assert.Equals(token, "abcd123456");
    }

验证调用UserManager<T>.CreateAsync的示例:

        // #### SETUP ####
        var _userManager = new Mock<UserManager<ApplicationUser>>()
            .Setup(um => um.CreateAsync(It.Is(user))
            .Verifiable("UserManager.CreateAsync wasn't called!");
        var _accountService = new AccountService(_userManager);
        // #### ACT ####
        var register = await _accountService.Register(_applicationUser, "password");
        // #### VERIFY ####
        // Verify that GenerateToken method has been called with correct parameters
        _userManager.Verify();
        // verify that the GenerateEmailConfirmationTokenAsync returned the expected token abc123456

这确保你的AccountServce.CreateAsync方法调用Identity。如果您稍后添加逻辑,这将确保在将来进行调用,或者如果您添加一些阻止CreateAsync被调用的逻辑,测试将失败。这个测试测试行为。

编辑

如果您不知道mock是如何工作的,请注意:UserManager<T>.CreateAsync的原始代码将永远不会执行。mock完全覆盖该方法(因此它只适用于具有virtual方法的接口和类!!),并跳过它的整个逻辑,只返回一个预定义的值(在设置mock期间在.Returns(...)方法中指定的值)。

模型是用来返回假的/预先确定的值,所以你有确定的方法来测试一个类或功能。在集成测试中,mock的值低于单元测试,在单元测试中,您只想测试特定的代码片段(单元),而不需要外部依赖项,如数据库、文件系统或网络