Recursive class fixtures in C# xUnit

Viewed 214

I want to do something like this to provide methods from test classes to other test classes using composition.

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

The test in SomeTestClass passes but the test in SomeConsumingClass fails with a message

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

It seems like a feature like this is not directly supported as it seems to be looking for a parameterless constructor. I intended to use this pattern for each test class, therefore I am looking for some good way to do something similar without too much boilerplate code. Any suggestions on how I could provide methods from the other test classes without inheritance?

EDIT: Added some additional examples how I imagine myself using this

2 Answers

From the xUnit documentation on Class Fixtures (emphasis added):

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.

One solution would be to move the CreateSomeData method to SomeService, and then change SomeConsumingClass so that it also implements IClassFixture<SomeService>.

However, it's worth pointing out this line from the documentation regarding when IClassFixture is appropriate:

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.

From the description you've provided, it doesn't seem clear to me that IClassFixture is necessary here, since all you really want is the ability to call CreateSomeData from different test classes. A dead-simple alternative would be to just move GetSomeData to a utility class that can be directly called from any test fixture that needs it.

So I played around with possible solutions and was able to come up with a solution which allows for identical behavior

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(...)
    }
}
Related