Search code examples
c#arraysunit-testingautofixture

How to use AutoData in unit tests to supply N objects of a type in test arguments?


I use AutoData in my xUnit unit tests. I occasionally have a need for a particular number of objects to be supplied to my tests. Consider the following class:

public class Recipient
{
    public void Receive(
        CallingBird bird1,
        CallingBird bird2,
        CallingBird bird3, 
        CallingBird bird4
        )
    {
        this.Bird1 = bird1;
        this.Bird2 = bird2;
        this.Bird3 = bird3;
        this.Bird4 = bird4;
    }

    public CallingBird Bird1 { get; private set; }
    public CallingBird Bird2 { get; private set; }
    public CallingBird Bird3 { get; private set; }
    public CallingBird Bird4 { get; private set; }
}

Without AutoData, I might write a test like this:

[Fact]
public void All_Birds_Are_Populated()
{
    var bird1 = new CallingBird();
    var bird2 = new CallingBird();
    var bird3 = new CallingBird();
    var bird4 = new CallingBird();
    var sut = new Recipient();

    sut.Receive(bird1, bird2, bird3, bird4);

    Assert.NotNull(sut.Bird1);
    Assert.NotNull(sut.Bird2);
    Assert.NotNull(sut.Bird3);
    Assert.NotNull(sut.Bird4);
}

Using AutoData in situations like this, I've been asking for an array of arrays of the object I need in order to get enough distinct instances (assume I need distinct instances) like this:

[Theory, Autodata]
public void All_Birds_Are_Populated(CallingBird[][] birds, Recipient sut)
{
        sut.Receive(birds[0][0], birds[0][1], birds[0][2] ,birds[1][0]);

        Assert.NotNull(sut.Bird1);
        Assert.NotNull(sut.Bird2);
        Assert.NotNull(sut.Bird3);
        Assert.NotNull(sut.Bird4);
    }
}

When you ask for arrays from AutoData, it gives you an array of 3 of those objects. So, if I need 4 of something, I could ask for 2 arrays, or an array of arrays (as shown), which in this example is more wasteful than asking for two arrays. It works, but I'm often asking for more instances to be supplied than I need. Imagine a situation in which the count is higher, the objects are more expensive to create, etc.

Can you suggest a cleaner way to ask for N objects of a type as a unit test parameter, where N is exactly the number I need?


Solution

  • Here's a proposed answer based on comments from Mark Seemann so far. I'll modify this as appropriate if this wasn't what he was hinting at...

    It seems I may have been overthinking things a bit. If I need 4 CallingBird instances for my SUT's method, then I can simply ask for those instances in separate parameters in the unit test signature like this:

    [Theory, Autodata]
    public void All_Birds_Are_Populated(
        CallingBird bird1,
        CallingBird bird2,
        CallingBird bird3,
        CallingBird bird4,
        Recipient sut)
    {
        sut.Receive(bird1, bird2, bird3, bird4);
    
        Assert.NotNull(sut.Bird1);
        Assert.NotNull(sut.Bird2);
        Assert.NotNull(sut.Bird3);
        Assert.NotNull(sut.Bird4);
    }
    

    If the parameter list gets too long, then it may be identifying a code smell in my SUT's method signature. If it's not a code smell, then I should be able to tolerate at least the same number of parameters in my test method as I do in my SUT's method.

    I suppose I could ask for arrays in the test method like in the OP to save space, but that's probably at the expense of showing clear intent.