如何在执行单元测试中更改方法的返回值

时间:2016-01-10 19:08:09

标签: c# unit-testing xunit.net nsubstitute

我试图设计一个单元测试来测试重试循环模式。我能想到的唯一方法就是在测试中途改变嵌入在重试循环中心的方法所返回的内容。

例如......我想在测试的前5秒内为特定方法抛出异常。然后停止抛出该异常,并在该点之后实际响应一些有效数据。

前5秒:

service.MethodToRetry(Arg.Any<string>()).ThrowsForAnyArgs(new Exception());

之后,异常条件被删除,MethodToRetry()正常完成。

这是可能的还是我完全以错误的方式解决这个问题?我在c#中使用xunit和nsubstitute。

2 个答案:

答案 0 :(得分:4)

注意:这里的测试是为了证明NSubstitute的行为。在实际测试中,我们不会测试替代品。 :)

测试重试的一种方法是存根多次返回(如果您需要条件而不是针对特定数量的调用失败,这可能不适用于您的情况,但我认为我会从最简单的方法开始):< / p>

    [Test]
    public void StubMultipleCalls() {
        Func<string> throwEx = () => { throw new Exception(); };
        var sub = Substitute.For<IThingoe>();
        // Stub method to fail twice, then return valid data
        sub.MethodToRetry(Arg.Any<string>())
           .Returns(x => throwEx(), x => throwEx(), x => "works now");

        // The substitute will then act like this:
        Assert.Throws<Exception>(() => sub.MethodToRetry("")); 
        Assert.Throws<Exception>(() => sub.MethodToRetry("")); 
        Assert.AreEqual("works now", sub.MethodToRetry(""));
        // Will continue returning last stubbed value...
        Assert.AreEqual("works now", sub.MethodToRetry(""));
        Assert.AreEqual("works now", sub.MethodToRetry(""));
    }

另一种选择是在你打断电话时把条件放进去:

    [Test]
    public void StubWithCondition() {
        var shouldThrow = true;

        var sub = Substitute.For<IThingoe>();
        sub.MethodToRetry(Arg.Any<string>()).Returns(x => {
            if (shouldThrow) {
                throw new Exception();
            }
            return "works now";
        });

        Assert.Throws<Exception>(() => sub.MethodToRetry(""));
        shouldThrow = false; // <-- can alter behaviour by modifying this variable
        Assert.AreEqual("works now", sub.MethodToRetry(""));
    }

作为此方法的修改版本,您还可以替换用于存根的回调:

    [Test]
    public void ReplaceLambda() {
        Func<string> methodToRetry = () => { throw new Exception(); };

        var sub = Substitute.For<IThingoe>();
        sub.MethodToRetry(Arg.Any<string>()).Returns(x => methodToRetry());

        Assert.Throws<Exception>(() => sub.MethodToRetry(""));
        methodToRetry = () => "works now";
        Assert.AreEqual("works now", sub.MethodToRetry(""));
    }

理想情况下,我们会尝试在测试中避免与时间相关的逻辑,但如果真的有必要,我们可以在5秒后更新第二个示例中的条件,以获得问题中提到的行为。

答案 1 :(得分:0)

所有的拳头,我没有看到任何具体的实施,所以我会把它放在一般。

思想:

  1. 因为你想测试一个“重试循环模式”,我假设你有“5秒等待”部分的逻辑。这个逻辑应该是一个可注入的调用,所以在你的测试中你可以检查它是否被调用。 (http://nsubstitute.github.io/help/received-calls/
  2. 等待部分不应该在您的测试中,因为您的方法必须等待重试步骤而不是测试。