xUnit提供了(共享(类固定装置的概念,如测试之间的共享上下文中所解释的。到目前为止,我还没有弄清楚是否有一种方法可以参数化此类固定装置。例如,如果DatabaseFixture
应该用一些测试数据来丰富,这取决于它所针对的测试,该怎么办?测试类可能希望插入测试数据,但只插入一次,然后对该数据库(fixture(运行所有测试。
换句话说,如果文档中的// ... initialize data in the test database ...
(如上所述(也取决于测试,该怎么办?因为并非所有测试都希望具有相同的测试数据。事实上,我甚至认为很多时候,测试定义自己的测试数据,而不是在测试数据级别上耦合测试,这是一种很好的做法。
到目前为止,我所做的变通方法是提供一个ConfiguredWith
方法,该方法接收只执行一次的回调。为了做到这一点,我需要延迟测试数据库的初始化,这样我就可以确定配置选项已经设置好了。类似于:
public class MyDatabaseTests : IClassFixture<DatabaseFixture>
{
DatabaseFixture fixture;
public MyDatabaseTests(DatabaseFixture fixture)
{
this.fixture = fixture;
this.fixture.ConfigureWith(new DatabaseFixtureOptions
{
InitTestData = db => db.Insert(...);
};
}
// ...
}
对于在针对数据库编写测试时感觉像是标准要求的东西来说,这看起来相当做作。
如果xUnit没有开箱即用,也许有人对如何解决这个问题有更好的模式。
这个问题似乎朝着类似的方向发展,但我不一定会确定一个具有这种结构的解决方案。
我艰难地了解到,试图通过IClassFixture
或CollectionFixtures
共享实体框架数据库上下文最终会导致测试被另一个测试数据污染,或者由于xUnit的并行执行而导致死锁/竞争条件,实体框架会抛出异常,因为它已经跟踪了具有给定Id的对象,还有更多类似的问题。就我个人而言,我建议出于您的特定使用原因,将数据库上下文创建/清理保留在constructor/dispose
替代方案中,例如:
public class TestClass : IDisposable
{
DatabaseContext DatabaseContext;
public TestClass()
{
var options = new DbContextOptionsBuilder<DatabaseContext>()
.UseInMemoryDatabase(databaseName: Guid.NewGuid().ToString())
.Options;
DatabaseContext = new DatabaseContext(options);
//insert the data that you want to be seeded for each test method:
DatabaseContext.Set<Product>().Add(new Product() { Id = 1, Name = Guid.NewGuid().ToString() });
DatabaseContext.SaveChanges();
}
[Fact]
public void FirstTest()
{
var product = DatabaseContext.Set<Product>().FirstOrDefault(x => x.Id == 1).Name;
//product evaluates to => 0f25a10b-1dfd-4b4b-a69d-4ec587fb465b
}
[Fact]
public void SecondTest()
{
var product = DatabaseContext.Set<Product>().FirstOrDefault(x => x.Id == 1).Name;
//product evaluates to => eb43d382-40a5-45d2-8da9-236d49b68c7a
//It's different from firstTest because is another object
}
public void Dispose()
{
DatabaseContext.Dispose();
}
}
当然,你总是可以做一些改进,但的想法就在那里
我没有回复Rod的名声,但我相信你可以通过将你的测试类添加到集合中来轻松纠正这个问题:
[收集("顺序"(]
公共类TestClass:IDisposable{…}
这将强制在多个类之间完成测试,每次一个,只要它们具有相同的集合名称。不过,我不确定如何在集合中实现fixture。不过,我确实找到了这篇文章:https://www.programmingwithwolfgang.com/xunit-getting-started/希望这会有所帮助。
我个人建议实现fixture,只要您使用Collection((注释进行集成测试。