C# xUnit 中的递归 class 装置

Recursive class fixtures in C# xUnit

我想做这样的事情来提供从测试 classes 到其他测试 classes 使用组合的方法。

public class SomeTestClass : IClassFixture<SomeService>
{
    private readonly SomeService SomeService;

    public SomeTestClass(SomeService someService)
    {
        SomeService = someService;
    }

    [Fact]
    private void Test()
    {
        //....
    }

    public SomeData CreateSomeData()
    {
       // populate fields with something based on internal/service state
        return new SomeData(); 
    }

    public void DoSomeAction(....)
    {
        // does action which modifies internal/service state
    }

}

public class SomeConsumingClass : IClassFixture<SomeTestClass>
{
    private readonly SomeTestClass SomeTestClass;

    public SomeConsumingClass(SomeTestClass someTestClass)
    {
        SomeTestClass = someTestClass;
    }

    [Fact]
    private void Test()
    {
        var data = SomeTestClass.CreateSomeData();
        // ....
        SomeTestClass.DoSomeAction(...)
    }
}

SomeTestClass 中的测试通过但 SomeConsumingClass 中的测试失败并显示消息

Class fixture type 'Requirements.SomeTestClass' had one or more unresolved constructor arguments: SomeService someService) (The following constructor parameters did not have matching fixture data: SomeTestClass someTestClass

似乎不​​直接支持这样的功能,因为它似乎在寻找无参数构造函数。我打算将此模式用于每个测试 class,因此我正在寻找一些好的方法来做类似的事情而无需太多样板代码。关于如何在没有继承的情况下从其他测试 classes 提供方法的任何建议?

编辑: 添加了一些额外的例子,我想象自己如何使用这个

来自xUnit documentation on Class Fixtures(强调已添加):

Note that you cannot control the order that fixture objects are created, and fixtures cannot take dependencies on other fixtures. If you have need to control creation order and/or have dependencies between fixtures, you should create a class which encapsulates the other two fixtures, so that it can do the object creation itself.

一个解决方案是将 CreateSomeData 方法移动到 SomeService,然后更改 SomeConsumingClass 以便它也实现 IClassFixture<SomeService>.

但是,值得从文档中指出有关 IClassFixture 何时合适的这一行:

When to use: when you want to create a single test context and share it among all the tests in the class, and have it cleaned up after all the tests in the class have finished.

根据您提供的描述,我似乎不清楚 IClassFixture 在这里是必需的,因为您真正想要的只是能够从不同的测试中调用 CreateSomeData classes。一个非常简单的替代方法是将 GetSomeData 移动到一个实用程序 class,可以从任何需要它的测试装置直接调用它。

所以我尝试了可能的解决方案,并且能够想出一个允许相同行为的解决方案

public class SomeTestClass
{
    private readonly SomeService SomeService;

    public SomeTestClass(SomeService someService)
    {
        SomeService = someService;
    }

    [Fact]
    private void Test()
    {
        //....
    }

    public SomeData CreateSomeData()
    {
        // populate fields with something based on internal/service state
        return new SomeData(); 
    }

    public void DoSomeAction(....)
    {
        // does action which modifies internal/service state
    }

}

public class SomeDerivingTestClass : SomeTestClass
{
    public SomeDerivingTestClass() : base(CreateSomeService())
    {
        
    }

    private static SomeService CreateSomeService()
    {
        return new SomeService();
    }
}

public class SomeConsumingClass : IClassFixture<SomeDerivingTestClass>
{
    private readonly SomeTestClass SomeTestClass;

    public SomeConsumingClass(SomeDerivingTestClass someTestClass)
    {
        SomeTestClass = someTestClass;
    }

    [Fact]
    private void Test()
    {
        var data = SomeTestClass.CreateSomeData();
        // ....
        SomeTestClass.DoSomeAction(...)
    }
}