繁体   English   中英

为 In-memory DbContext 设置 Autofixture

[英]Setting up an Autofixture for In-memory DbContext

我目前正在尝试使用 Autofixture 创建一个预定义的装置,作为使用In-Memory provider 的ApplicationDbContext 的ICustomization的实现。

public class ApplicationDbContextFixture : ICustomization
{
    public void Customize(IFixture fixture)
    {
        var specimenFactory = new SpecimenFactory<ApplicationDbContext>(CreateDbContext);
        fixture.Customize<ApplicationDbContext>(
                composer =>
                    composer.FromFactory(specimenFactory)
                );
    }

    /// <summary>
    /// Private factory method to create a new instance of <see cref="ApplicationDbContext"/>
    /// </summary>
    private ApplicationDbContext CreateDbContext()
    {
        var dbContextOptions = new DbContextOptionsBuilder<ApplicationDbContext>()
                    .UseInMemoryDatabase("SomeDatabaseName")
                    .Options;
        var dbContext = new ApplicationDbContext(dbContextOptions);        
        return dbContext;
    }
}

然后,我将按如下方式将该自定义应用到我的 Fixture:

    [Fact]
    public void TestAddUsersToEmptyDatabase()
    {
        // Arrange
        // Fixture for ApplicationDbContext
        var fixture = FixtureFactory.CreateFixture();
        var applicationDatabaseFixture = new ApplicationDbContextFixture();
        fixture.Customize(applicationDatabaseFixture);

        // Fixture for users
        var randomUser = fixture.Create<AppUser>();
        var normalUser = fixture.Create<AppUser>();
        var adminUser = fixture.Create<AppUser>();

        // Act & Assert
        // Run the test against one instance of the context
        // Use a clean instance of the context for each operation too
        using (var dbContext = fixture.Create<ApplicationDbContext>())
        {
            Assert.Empty(dbContext.Users);
            dbContext.Users.Add(randomUser);
            dbContext.SaveChanges();
        }

        using (var dbContext = fixture.Create<ApplicationDbContext>())
        {
            dbContext.Users.AddRange(normalUser, adminUser);
            dbContext.SaveChanges();
        }

        using (var dbContext = fixture.Create<ApplicationDbContext>())
        {
            Assert.NotEmpty(dbContext.Users);
            Assert.NotNull(dbContext.Users.SingleOrDefault(_ => _.Id == randomUser.Id));
            Assert.NotNull(dbContext.Users.SingleOrDefault(_ => _.Id == normalUser.Id));
            Assert.NotNull(dbContext.Users.SingleOrDefault(_ => _.Id == adminUser.Id));
        }
    }

FixtureFactory.CreateFixture 实现

    /// <summary>
    /// Factory method to declare a single <see cref="IFixture"/> for unit tests applications
    /// </summary>
    internal static class FixtureFactory
    {
        internal static IFixture CreateFixture()
        {
            var fixture = new Fixture().Customize(
                new AutoMoqCustomization { ConfigureMembers = true });

            return fixture;
        }
    }

现在在我的单元测试中,断言Assert.Empty(dbContext.Users); 将抛出System.NotImplementedException : The method or operation is not implemented. 因为从 Autofixture 生成的DbSet<AppUser> Users是一个 DynamicProxy。

请参阅图像dbContext.Users 作为 DynamicProxy

奇怪的是,如果我检查从CreateDbContext() fixture.Create<ApplicationDbContext>()调用的工厂方法(即CreateDbContext() )的断点, DbSet Users 是预期的类型。

请参阅图像dbContext.Users 作为 InternalDbSet

或者,我知道我可以将dbContext.Users所有用法dbContext.UsersdbContext.Set<User>()并且这将使单元测试通过但问题是在实际类中,我使用的是dbContext.Users对于IQueryables和数据库操作,所以如果可能的话,我仍然需要坚持下去。

因此,我需要帮助来了解为什么 AutoFixture 使用我的工厂方法为我的 ApplicationDbContext 生成实例,但是当 ISpecimenBuilder 解析时,其中的所有DbSet<>属性都被模拟。 有没有办法解决这个问题?

我已经在他们的 Github 中发布了类似的问题,但最近它不活跃,所以我也在这里问。

请理解我2天前才开始使用Autofixture。 因此,如果我写错了某些东西,或者对任何设计模式有误解,请写评论,以便我可以将其作为教训。

更新 1 :所以我尝试使用没有任何 AutoMoq 自定义的初始化普通夹具(即fixture = new Fixture() ),这次它抛出AutoFixture.ObjectCreationExceptionWithPath异常,抱怨它无法解析 ApplicationDbContext 中的 DbSet 属性。 在这一点上,我在想是否有人知道如何使用 Relay 或 ISpecimenBuilder 来告诉 Autofixture 使用/调用/实现 ApplicationDbContext 中的所有DbSet<T>属性与dbContext.Set<T>因为如果我替换所有在我的单元测试中使用 DbSets,但正如我提到的,所有 IQueryable 都是从 DbSets 返回的,所以我不能简单地在 ApplicationDbContext 中替换它。

更新 2 :我从我的工厂方法 CreateDbContext() 中删除并简化了 ApplicationDbContext 的创建,因为它会导致代码复杂性的混淆。

很难理解你想从你的帖子中实现什么。

我认为您真正需要的是测试碰巧使用 EntityFramework 的代码。 如果是这种情况,您可能想看看这个库,我创建了EntityFrameworkCore.AutoFixture 它使用内存中数据库提供程序以及 SQLite 内存中提供程序。

查看一些代码示例的自述文件。 如果您有任何问题,请给我留言或在 GitHub 上打开问题。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM