如何在单元测试中使用AutoData在测试参数中提供类型的N个对象?

Jef*_*eff 4 c# arrays unit-testing autofixture

我在xUnit单元测试中使用AutoData.我偶尔需要为我的测试提供特定数量的对象.考虑以下课程:

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; }
}
Run Code Online (Sandbox Code Playgroud)

没有AutoData,我可能会写一个这样的测试:

[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);
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下使用AutoData,我一直在寻找一个我需要的对象数组数组,以便获得足够多的不同实例(假设我需要不同的实例),如下所示:

[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);
    }
}
Run Code Online (Sandbox Code Playgroud)

当您从AutoData请求数组时,它会为您提供其中3个对象的数组.所以,如果我需要4个东西,我可以要求2个数组,或者一个数组数组(如图所示),在这个例子中比要求两个数组更浪费.它有效,但我经常要求提供比我需要的更多的实例.想象一下,计数越高,对象创建成本越高等情况.

您能否建议一种更简洁的方式来请求类型的N个对象作为单元测试参数,其中N正是我需要的数字?

Mar*_*ann 7

Lumirris 自己的答案是最好的答案,因为它解释了编写单元测试所提供的学习和反馈机会.

但是,我想提供一个替代方案,只是为了完整起见,但我不认为这应该是公认的答案.

使用AutoFixture,您可以要求a Generator<T>,这是一个IEnumerable<T>通过提供无限(懒惰评估)元素序列来实现的类.它使您能够获取有限的,已知数量的元素:

[Theory, Autodata]
public void All_Birds_Are_Populated(
    Generator<CallingBird> g,
    Recipient sut)
{
    var birds = g.Take(4).ToList();

    sut.Receive(birds[0], birds[1], birds[2], birds[3]);

    Assert.NotNull(sut.Bird1);
    Assert.NotNull(sut.Bird2);
    Assert.NotNull(sut.Bird3);
    Assert.NotNull(sut.Bird4);
}
Run Code Online (Sandbox Code Playgroud)


Jef*_*eff 5

以下是基于Mark Seemann评论的建议答案.如果不是他暗示的话,我会酌情修改这个......

看来我可能已经过度思考了一些事情.如果CallingBird我的SUT方法需要4个实例,那么我可以简单地在单元测试签名中的单独参数中询问这些实例,如下所示:

[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);
}
Run Code Online (Sandbox Code Playgroud)

如果参数列表太长,那么它可能在我的SUT方法签名中识别代码气味.如果它不是代码味道,那么我应该能够容忍测试方法中至少与我在SUT方法中相同数量的参数.

我想我可以在测试方法中请求数组,就像在OP中节省空间一样,但这可能是以显示明确意图为代价的.