Typesafety in xUnit with TheoryData<T>

05/03/2024
.NETC#xUnit

I recently discovered this small but very useful utility in xUnit: TheoryData<T>.

The problem

I had the following code in my repository:

public static IEnumerable<object[]> Data => new List<object[]>
    {
        new object[] { new Action<IServiceCollection>(services => services.UseSqliteAsStorageProvider()) },
        new object[] { new Action<IServiceCollection>(services => services.UseSqlAsStorageProvider()) },
        new object[] { new Action<IServiceCollection>(services => services.UseInMemoryAsStorageProvider()) },
        new object[] { new Action<IServiceCollection>(services => services.UseRavenDbAsStorageProvider()) },
        new object[] { new Action<IServiceCollection>(services => services.UseMySqlAsStorageProvider()) },
    };

[Theory]
[MemberData(nameof(Data))]
public void GivenAlreadyRegisteredRepository_WhenTryingToAddAnotherStorage_ThenException(Action<IServiceCollection> act)

The issue her eis that Data is of type IEnumerable<object[]> and the act parameter is of type Action<IServiceCollection>. This means that I can pass anything to the method and the compiler will happily accept it. Here is where the TheoryData<T> comes in.

The solution

TheoryData<T> is a class that allows to wrap the data in a strongly typed way. Here is how I used it:

public static TheoryData<Action<IServiceCollection>> Data => new() 
{
    services => services.UseSqliteAsStorageProvider(),
    services => services.UseSqlAsStorageProvider(),
    services => services.UseInMemoryAsStorageProvider(),
    services => services.UseRavenDbAsStorageProvider(),
    services => services.UseMySqlAsStorageProvider()
};

[Theory]
[MemberData(nameof(Data))]
public void GivenAlreadyRegisteredRepository_WhenTryingToAddAnotherStorage_ThenException(Action<IServiceCollection> act)

Exactly the same test, but way more typesafe. Now the compiler will not allow me to pass anything other than Action<IServiceCollection> to the method.

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