DEV Community

Chris Missal
Chris Missal

Posted on

Testing "ALL" Scenarios with xUnit

Extending xUnit

If you've used xUnit, you've probably also used some other libraries that work well with it, or even add to its functionality. I've been using an extension library for a few years now and it's constantly in my tool-belt.

Examples from Xunit.Combinatorial

Suppose you have this test method:

[Fact]
public void CheckFileSystem(bool recursive)
{
    // verifications here
}

To arrange for your test method to be invoked twice, once for each value
of its bool parameter, change the attributes to

[Theory, CombinatorialData]
public void CheckFileSystem(bool recursive)
{
    // verifications here
}

The CombinatorialDataAttribute will supply Xunit with both true and false
arguments to run the test method with, resulting in two invocations of your
test method with individual results reported for each invocation.

To supply your own values to pass in for each parameter, use the
CombinatorialValuesAttribute:

[Theory, CombinatorialData]
public void CheckValidAge([CombinatorialValues(5, 18, 21, 25)] int age)
{
    // verifications here
}

This will run your test method four times with each of the prescribed values.

Extending Xunit.Combinatorial

Another library I've used for many years that always gets the job done is Enumeration from Headspring.

By extending the CombinatorialValuesAttribute class, you can easily make a test method that injects every single Enumeration into your your test method.

If given this Enumeration sample from GitHub

public class Role : Enumeration<Role, int>
{
    public static readonly Role System = new Role(999, "System", "System", true);
    public static readonly Role Manager = new Role(1, "Manager", "Michelle", false);
    public static readonly Role Employee = new Role(2, "Employee", "Eric", false);
    public static readonly Role HR = new Role(3, "Human Resources", "Harry", false);

    private Role(int value, string displayName, string personaName, bool testrole)
        : base(value, displayName)
    {
        PersonaName = personaName;
        TestRole = testrole;
    }

    public string PersonaName { get; private set; }
    public bool TestRole { get; private set; }

    public static IEnumerable<Role> GetAllProductionRoles()
    {
        return GetAll().Where(r => !r.TestRole);
    }
}

... we could create two small types that let us test the different roles, and make different assertions.

public class AllRolesAttribute : CombinatorialValuesAttribute
{
    public AllRolesAttribute() :
        base(Role.GetAll())
    {
    }
}

public class AllProductionRolesAttribute : CombinatorialValuesAttribute
{
    public AllProductionRolesAttribute()
        :base(Role.GetAllProductionRoles().ToArray())
    {
    }
}

Pretty simple, yeah? But this now readily lets us TEST ALL THE THINGS:

public class RolesTests
{
    [Theory]
    [CombinatorialData]
    public void Some_test_for_all_Roles([AllRoles] Role role)
    {
        // test method will execute for each role in the Enumeration
    }

    [Theory]
    [CombinatorialData]
    public void Some_test_for_all_production_Roles([AllProductionRoles] Role role)
    {
        // test method will execute for each non-test role in the Enumeration
    }
}

That's about it, if you have questions or want to chat more, please reach out!

Top comments (0)