xUnit: How it runs IClassFixture and Collections

16/06/2024
C#xUnit

xUnit has a fantastic feature in which we can share some context for all tests inside a collection, In default every class is a collection for xUnit until we put [Collection] above a class.

Class Fixture is for: '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.'

Let's we have a class like below:

public class UserServiceTest
{
    private readonly string userId;

    public UserServiceTest()
    {
        userId = Guid.NewGuid().ToString();
    }

    [Fact]
    public void Test1() { /*code*/ }

    [Fact]
    public void Test2() { /*code*/ }

    [Fact]
    public void Test3() { /*code*/ }
}

In a normal way without using ClassFixture, xUnit will create a class instance per test which makes sense for the sake of test isolation but it's a common use case that you want to instantiate a class/service only once to be reused for all tests, then ClassFixture comes to the picture. So in the code above you may want to have only one userId for all tests, but with the current implementation, the userId will 3 different values, we can achieve this by using IClassFixture.

Let's create another class for saving our user related info:

public class UserDataSource
{
    private readonly string userId;

    public UserDataSource()
    {
        userId = Guid.NewGuid().ToString();
    }

    public string GetUserId() => userId;
}

So here in the ctor we generate a random value for the userId but we need to make sure it runs only once, here is the way:

public class UserServiceTest : IClassFixture<UserDataSource>
{
    private readonly string userId;

    public UserServiceTest(UserDataSource dataSource)
    {
        userId = dataSource.GetUserId(); // here userId is already generated in the UserDataSource class
    }

    [Fact]
    public void Test1() { /*code*/ }

    [Fact]
    public void Test2() { /*code*/ }

    [Fact]
    public void Test3() { /*code*/ }
}

So what's the point here?

I can see a misunderstanding between using IClassFixture and Class instantiating, this is the rule to be reminded of:

For one collection the IClassFixture runs once and class ctor runs per test.

Code Simple!

1
An error has occurred. This application may no longer respond until reloaded. Reload x