[英]What are the principles behind AutoFixture's declarative way of setting up a fixture?
[英]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.Users
为dbContext.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.