如何参数化 xunit 类装置?

Dej*_*jan 9 c# xunit xunit.net

xUnit 提供了(共享)类装置的概念,如测试之间的共享上下文中所述。到目前为止我还没有弄清楚是否有一种方法可以对此类装置进行参数化。例如,如果DatabaseFixture应该使用一些测试数据来丰富该数据,而这些数据取决于它所针对的测试,该怎么办?测试类可能想要插入测试数据,但只插入一次,然后针对该数据库(夹具)运行所有测试。

换句话说,如果// ... 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(...);
         };
    }

    // ... 
}
Run Code Online (Sandbox Code Playgroud)

对于针对数据库编写测试时感觉像是标准要求的东西来说,这看起来相当人为。

如果 xUnit 不提供开箱即用的功能,也许有人有更好的模式来解决这个问题。

这个问题似乎朝着类似的方向发展,但我不一定固定在具有该结构的解决方案上。

Rod*_*rez 0

我学到了一个艰难的方法,即尝试共享实体框架数据库上下文,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();
        }
    }
Run Code Online (Sandbox Code Playgroud)

当然你总是可以做一些改进,但想法就在那里

  • 谢谢你的努力。您的建议很简单,因此很强大,在许多情况下应该是如何针对数据库上下文进行冒烟测试的默认建议。然而,这对我来说并不新鲜,它仍然不是我的答案。在某些情况下,我们有更复杂的设置(=更高级别的系统测试),这也是我们想要共享参数化设置的原因。 (2认同)