如何参数化 xunit 类夹具? [英] How to parametrize a xunit class fixture?

查看:35
本文介绍了如何参数化 xunit 类夹具?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

xUnit offers the concept of (shared) class fixtures as explained in Shared Context between Tests. What I didn't figure out so far is if there is a way of parametrizing such class fixtures. For example, what if the DatabaseFixture should be enriched with some test data which depends on the test it's being run against? A test class might want to insert test data but only once and then run all its tests against that database (fixture).

换句话说,如果文档中的 //... 初始化测试数据库中的数据 ... 也依赖于测试怎么办?因为并非所有测试都可能希望拥有相同的测试数据.实际上,我什至认为很多时候测试定义自己的测试数据而不是在测试数据层面上耦合测试是一种很好的做法.

In other words, what if the // ... initialize data in the test database ... from the documentation (referenced above) also depends on the test? Because not all tests might want to have the same test data. Actually, I even think that many times it's good practice that tests define their own test data to not couple tests on the level of test data.

就解决方法而言,我正在做的是提供一个 ConfiguredWith 方法,该方法接受只执行一次的回调.为了做到这一点,我需要延迟测试数据库的初始化,以便确定配置选项已设置.类似的东西:

What I'm doing so far as workaround is to offer a ConfiguredWith method that takes in a callback that is only being executed once. And in order to do so, I need to lazily postpone the initialization of the test database so that I'm sure that the configuration options are set. Something like:

public class MyDatabaseTests : IClassFixture<DatabaseFixture>
{
    DatabaseFixture fixture;

    public MyDatabaseTests(DatabaseFixture fixture)
    {
         this.fixture = fixture;
         this.fixture.ConfigureWith(new DatabaseFixtureOptions
         {
             InitTestData = db => db.Insert(...);
         };
    }

    // ... 
}

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

And this looks rather contrived for something that feels like a standard requirement when writing tests against a database.

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

And if xUnit doesn't offer this out-of-the-box, maybe someone has a better pattern on how to solve this.

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

This question seems to go in a similar direction but I'm not necessarily fixed on a solution that has that structure.

推荐答案

我学到了尝试通过 IClassFixtureCollectionFixtures 共享实体框架数据库上下文的艰难方法由于 xUnit 的并行执行,最终导致测试被另一个测试数据或死锁/竞争条件污染,实体框架抛出异常,因为它已经用给定的 Id 跟踪了该对象以及更多类似的问题.就个人而言,我建议您针对您的特定使用原因,将数据库上下文创建/清理放在 constructor/dispose 替代方案中,例如:

I learn the hard way that trying to share the entity framework database context over IClassFixture or CollectionFixtures would eventually end up in tests being polluted with another test data or deadlock/race conditions due to the parallel execution of xUnit, entity framework throwing exceptions because it already tracked that object with a given Id and more headaches like that. Personally, I would kindly recommend that for your specific use cause, stick the database context creation/cleanup within the constructor/dispose alternative such as:

    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();
        }
    }

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

Of course you can always do some refinement, but the idea is there

这篇关于如何参数化 xunit 类夹具?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆