【问题标题】:How to parametrize a xunit class fixture?如何参数化 xunit 类夹具?
【发布时间】:2020-11-12 08:15:45
【问题描述】:

xUnit 提供了(共享)类固定装置的概念,如Shared Context between Tests 中所述。到目前为止,我还没有弄清楚是否有一种方法可以对此类固定装置进行参数化。例如,如果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(...);
         };
    }

    // ... 
}

这看起来相当做作,因为在针对数据库编写测试时感觉像是标准要求。

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

This question 似乎朝着类似的方向发展,但我不一定会固定在具有这种结构的解决方案上。

【问题讨论】:

  • 嗨,Dejan,你好吗?为什么使用TheoryMemberDataClassData 不能满足您的需求?在我看来,您正在寻找的是一组依赖于测试(MemberData/ClassData)而不是依赖于测试类的数据(共享上下文:ClassFixture/CollectionFixture
  • @RodRamírez 人们在创建测试数据成本高昂(例如:准备物理数据库)时使用类/集合夹具,以便可以跨测试共享此夹具。最重要的是,我想看看是否有一种方便的方法可以以参数化的方式初始化数据库中的测试数据。
  • 如果您希望所有测试在开始之前都拥有相同的数据集,您应该在下面查看我的答案。现在,关于与给定测试类中的每个测试相关的数据集,您可以查看 memberData/ClassData 方法。 @Dejan
  • 嘿,Dejan,想知道您是否找到了实现这一目标的好方法?谢谢

标签: c# xunit xunit.net


【解决方案1】:

我了解到,由于 xUnit 的并行执行,尝试通过 IClassFixtureCollectionFixtures 共享实体框架数据库上下文最终会导致测试被另一个测试数据或死锁/竞争条件污染,实体框架抛出异常,因为它已经使用给定的 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();
        }
    }

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

【讨论】:

  • 感谢您的努力。您的建议很简单,因此功能强大,并且在许多情况下应该是如何针对数据库上下文进行冒烟测试的默认设置。然而,这对我来说并不是什么新鲜事,它仍然不是我的答案。在某些情况下,我们有一个更复杂的设置(= 更高级别的系统测试),这也是我们想要共享参数化设置的原因。
  • 我明白了。我也无法在网络上的任何地方找到适合您需求的东西。但是,我记得我曾经在使用 xUnit 框架时遇到过一些问题,并且支持者有一个 slack 频道,您可以在其中找到比我更喜欢这种场景的程序员,您可能会发现这很有帮助
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-06-27
  • 2023-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-02
  • 1970-01-01
相关资源
最近更新 更多