C# NSsubstitute ordered testing(received.inoorder)与调用方法的返回值一起导致CouldNotSetReturnDueTomissingInfo关于调用调用调用异常

C# NSsubstitute ordered testing(received.inoorder)与调用方法的返回值一起导致CouldNotSetReturnDueTomissingInfo关于调用调用调用异常,c#,nsubstitute,C#,Nsubstitute,我以前用Rhinomock编写测试,现在改为NSubstitute。 现在我有一个关于有序测试的问题。 假设我有三个小班,比如 public interface IProvider { int GetData(); } public class Provider : IProvider { public int GetData() { return 3; } } public interface ICalculator { int Ca

我以前用Rhinomock编写测试,现在改为NSubstitute。 现在我有一个关于有序测试的问题。 假设我有三个小班,比如

public interface IProvider
{
    int GetData();
}

public class Provider : IProvider
{
    public int GetData()
    {
        return 3;
    }
}

public interface ICalculator
{
    int Calculate(int data);
}

public class Calculator : ICalculator
{
    public int Calculate(int data)
    {
        if (data < 3)
        {
            return data;
        }

        return data * 2;
    }
}

public class Operator
{
    public void Operate(IProvider provider, ICalculator calculator)
    {
        int version = provider.GetData();

        this.Result = calculator.Calculate(version);
    }

    public int Result
    {
        get;
        private set;
    }
}
公共接口IProvider
{
int GetData();
}
公共类提供程序:IProvider
{
公共int GetData()
{
返回3;
}
}
公共接口计算器
{
int计算(int数据);
}
公共类计算器:ICalculator
{
公共整数计算(整数数据)
{
如果(数据<3)
{
返回数据;
}
返回数据*2;
}
}
公共类运算符
{
公共无效操作(IProvider提供程序、ICalculator计算器)
{
int version=provider.GetData();
this.Result=calculator.Calculate(版本);
}
公共整数结果
{
得到;
私人设置;
}
}
当我使用Rhinomock编写有序测试时,我可以定义模拟类的行为,如下所示:

[Test]
    public void RhinoMockOrderedTest()
    {
        var mockRepository = new MockRepository();
        var provider = mockRepository.DynamicMock<IProvider>();
        var calculator = mockRepository.DynamicMock<ICalculator>();

        using (mockRepository.Ordered())
        {
            provider.Expect(p => p.GetData()).Return(4);
            calculator.Expect(c => c.Calculate(4)).Return(9);
        }

        mockRepository.ReplayAll();

        var op = new Operator();
        op.Operate(provider, calculator);

        mockRepository.VerifyAll();

        Assert.That(op.Result, Is.EqualTo(9));
    }
[测试]
public-void-mockorderedtest()
{
var mockRepository=new mockRepository();
var provider=mockRepository.DynamicMock();
var calculator=mockRepository.DynamicMock();
使用(mockRepository.Ordered())
{
Expect(p=>p.GetData()).Return(4);
计算器.Expect(c=>c.Calculate(4)).Return(9);
}
mockRepository.ReplayAll();
var op=新运算符();
操作(提供者、计算器);
mockRepository.VerifyAll();
断言(op.Result,Is.EqualTo(9));
}
现在,我正在尝试使用NSubstitute编写一个类似于上面的有序测试,其中我还尝试检查调用顺序并使用定义的返回值:

[Test]
    public void NSubstituteOrderedTest()
    {
        var provider = Substitute.For<IProvider>();
        var calculator = Substitute.For<ICalculator>();

        var op = new Operator();
        op.Operate(provider, calculator);

        Received.InOrder(() =>
        {
            provider.GetData().Returns(4);
            calculator.Calculate(4).Returns(9);
        });

        Assert.That(op.Result, Is.EqualTo(9));
    }
[测试]
公共无效NSSubstituteOrderedTest()
{
var provider=Substitute.For();
var计算器=替换为();
var op=新运算符();
操作(提供者、计算器);
已收到。按顺序(()=>
{
GetData()返回(4);
计算器。计算(4)。返回(9);
});
断言(op.Result,Is.EqualTo(9));
}
不幸的是,这不起作用。在我看来,当我尝试在Received.inoder-Action中使用.Returns作为methon时,它总是会失败,如下所示:

[Test]
    public void RhinoMockOrderedTest()
    {
        var mockRepository = new MockRepository();
        var provider = mockRepository.DynamicMock<IProvider>();
        var calculator = mockRepository.DynamicMock<ICalculator>();

        using (mockRepository.Ordered())
        {
            provider.Expect(p => p.GetData()).Return(4);
            calculator.Expect(c => c.Calculate(4)).Return(9);
        }

        mockRepository.ReplayAll();

        var op = new Operator();
        op.Operate(provider, calculator);

        mockRepository.VerifyAll();

        Assert.That(op.Result, Is.EqualTo(9));
    }
NSSubstitute.Exceptions.CouldNotSetReturns由于缺少有关调用异常的信息 :找不到有关上次从中返回的呼叫的信息

确保在调用替换项(对于)后调用了Returns() 示例:mySub.SomeMethod().Returns(value)),而您不是 在Returns()中配置其他替代项(例如,避免 this:mySub.SomeMethod().返回(ConfigOtherSub())

如果替换的是类而不是接口,请检查 对您的替代者的呼叫是针对虚拟/抽象成员的。返回 无法为非虚拟/非抽象成员配置值

正确使用:mySub.SomeMethod().Returns(returnValue)

可能存在问题的使用: mySub.SomeMethod().返回(ConfigOtherSub());相反,请尝试:var returnValue=ConfigOtherSub(); mySub.SomeMethod().Returns(returnValue)

如何使用NSubstitute编写此测试

谢谢


Nico

NSubstitute在这里的工作方式与Rhino mock不同——它只支持Arrange-Act-Assert(AAA)样式的测试。这意味着我们需要剔除我们感兴趣的调用(排列),运行我们想要测试的代码(act),然后断言结果是预期的(断言)

仅适用于断言,并且对于每个调用都像NSubstitute一样工作。安排调用以返回特定结果。NSubstitute不允许我们将两者混合。我们不能执行
sub.Received().Calculate().Returns(42)
,而且在AAA中没有意义,因为在断言我们已经对被测试的对象采取了行动并收到了所有必需的调用之后,存根返回值没有什么意义

以下是将存根/排列与断言分开的问题的测试通过版本:

[Test]
public void NSubstituteOrderedTest() {
    // Arrange
    var provider = Substitute.For<IProvider>();
    var calculator = Substitute.For<ICalculator>();

    provider.GetData().Returns(4);
    calculator.Calculate(4).Returns(9);

    // Act
    var op = new Operator();
    op.Operate(provider, calculator);

    // Assert
    Received.InOrder(() =>
    {
        provider.GetData();
        calculator.Calculate(4);
    });
    Assert.That(op.Result, Is.EqualTo(9));
}
[测试]
公共无效NSSubstituteOrderedTest(){
//安排
var provider=Substitute.For();
var计算器=替换为();
GetData()返回(4);
计算器。计算(4)。返回(9);
//表演
var op=新运算符();
操作(提供者、计算器);
//断言
已收到。按顺序(()=>
{
GetData();
计算器。计算(4);
});
断言(op.Result,Is.EqualTo(9));
}

旁白:我知道这是一个简化的例子,但我认为值得注意的是,在许多情况下,我们可以不测试呼叫顺序而逃脱。对于这个简单的例子,我们知道首先调用
GetData()
,因为它的值被传递到
Calculate()
,因此通过数据依赖关系强制执行顺序。如果最终结果是正确的,我们就知道调用链是正确的。对于更复杂的情况,我们可以为此使用类型(
Connect()
返回
ConnectedDb
,然后
Query(ConnectedDb)
确保首先调用
Connect()

依赖于对被测试代码的实现细节的了解(例如调用顺序)可能会导致脆弱的测试(即,它们会因不应影响总体结果的微小更改而失败),因此最好尽可能避免这种情况

然而,即使有此免责声明,有时也可以使用断言调用顺序,因此我希望此答案为您清除此NSubstitute功能。:)