0

我正在使用 ASP.NET Core 3.1 和 XUnit 进行单元测试。

我构建了一个数据库上下文工厂类,它实例化了我的数据库的内存版本:

public static class DbContextFactory
{
    public static ApplicationDbContext CreateDbContext()
    {
        var options = new DbContextOptionsBuilder<ApplicationDbContext>()
            .UseInMemoryDatabase(Guid.NewGuid().ToString())
            .Options;

        var modelBuilder = new ModelBuilder(new ConventionSet());

        var dbContext = new ApplicationDbContext(options);

        var onModelCreatingMethod = dbContext.GetType().GetMethod("OnModelCreating",
            BindingFlags.Instance | BindingFlags.NonPublic);

        onModelCreatingMethod.Invoke(dbContext,
            new object[] { modelBuilder });

        return dbContext;
    }
}

这是我正在尝试使用的当前测试类:

public class AdminServiceTests
{
    public ApplicationDbContext context { get; set; }
    public IAdminService adminService { get; set; }

    public AdminServiceTests()
    {
        this.context = DbContextFactory.CreateDbContext();
        this.adminService = new AdminService(userManager, context);
    }

    [Fact]
    public async Task DeleteUserShouldDeleteUser()
    {
        // What to do ???
    }
}

为了让我测试我的管理服务,我需要提供一个用户管理器。它应该与我当前创建的数据库链接。

我怎样才能做到这一点?

4

1 回答 1

0

您在测试框架时犯了一个常见错误。您的测试需要做的就是确保AdminService.DeleteUser调用UserManager.DeleteAsync. 这是否会导致实际从数据库中删除用户是 1) 不是服务的问题,以及 2) ASP.NET Core Identity 和 EF Core 的实现细节,它们都有自己的广泛的测试套件来确保发生这种情况。

因此,您可以使用像 Moq 这样的库来创建模拟,UserManager<TUser>然后执行以下操作:

userManagerMock.Verify(x => x.DeleteAsync(user), Times.Once());

这里值得一提的是,这也有助于指出这种设计中的一些缺陷。无论您是否在其AdminService周围放置包装器,您都依赖于 ASP.NET Core Identity。除非您的服务在代理到这里之外做了一些特殊的事情UserManager(例如协调多个操作,例如删除用户会触发通知或其他东西),否则您的服务毫无意义,您应该直接使用UserManager。开发者不断犯这种错误;为了抽象而抽象只会伤害你的代码。它增加了额外的维护问题、测试问题,并掩盖了代码实际在做什么。

于 2020-04-01T13:20:22.630 回答