在When..Do for void方法中预期Arg.Do()没有被触发

时间:2011-09-28 17:58:00

标签: c# nsubstitute

我在我的测试中有以下结构,旨在测试使用正确的复杂参数对象调用某个日志,即使它抛出异常然后被包装并进一步操作。 logThing有一个方法:

void AddEntry(LogEntry);

所以我使用When..Do使它抛出异常,

public void UnitTest()
{
    // Arrange
    ILogThing logThing = Substitute.For<ILogThing>()
    SystemUnderTest system = new SystemUnderTest(logThing);

    List<LogEntry> actualEntries = new List<LogEntry>();
    LogEntry expectedEntry = GetSomeTestData();

    logThing.When(
        lt => lt.AddEntry(Arg.Do<LogEntry>(r => actualEntries.Add(r)))).Do(
        call => { throw new InvalidOperationException("testMessage"); });

    // Act
    try
    {
        system.DoSomethingWhichLogs(someArgs)
    }
    catch(WrappedException ex)
    {
        // Assert
        Assert.AreEqual(1, actualEntries.Count);
        Assert.AreEqual(actualEntries[0], expectedEntry);
    }
}

但是,此设置不会发生对Arg.Do()的预期调用。

我在catch块中放了一个断点,并使用Visual Studio的即时窗口在logThing上调用RecievedCalls&lt;&gt;(),并且它确实记录了使用正确参数对logThing的单个调用 - 它只是Arg.Do似乎只在When..Do块完成后执行。显然,这意味着,因为我投入了When..Do,它永远不会到达它。

我真的没想到NSubstitute以这种方式订购电话,这是预期的行为吗? 如果是这样,有什么我可以做的来测试这样的传入参数,或者我应该把我的参数检查到主要的When..Do块(这使得它更难阅读)?

被测系统对异常做了各种各样的事情,包括将它与logEntry一起包装,所以它对我来说在一次测试中完成所有这些检查是有用的 - 我确实考虑过将它分成两个独立的测试,但意识到,如果我这样做,我不能轻易确定不正确的包装输出来自哪里(它可能是最初生成logEntry的部分,或包裹它的部分)wheras有这种模式我可以检查确保logThing正在接收我期望的内容。不过,如果有更好的方法,我肯定愿意接受建议。

1 个答案:

答案 0 :(得分:2)

没有定义When..Do和Arg.Do的确切顺序,我不建议指望它,因为我想它可能会根据实现在版本之间进行更改。 (如果您有充分的理由按特定顺序定义,请将您的建议发布到usergroup。)

如果您只想检查logThing收到预期的LogEntry,您可以使用Arg.Is()在事后检查参数:

logThing.Received().AddEntry(Arg.Is(expectedEntry));

如果您需要更复杂的比较逻辑,您可以执行相同的操作,但使用方法检查参数:

logThing.Received().AddEntry(Arg.Is<LogEntry>(x => CheckLogEntry(x)));

CheckLogEntry可以执行您需要的任何检查,并且可以像以前一样保持When..Do投掷。

如果您需要使用When..Do方法,您可以保留现有方法,但将参数捕获移至Do调用,以确保您期望的调用顺序:

logThing.When(
    lt => lt.AddEntry(Arg.Any<LogEntry>())).Do(
    call =>
    {
        actualEntries.Add(call.Arg<LogEntry>());
        throw new InvalidOperationException("testMessage");
    });

就其他测试方法的建议而言,我并不完全清楚你要在这里做什么,但如果你在隔离包装输出的来源方面遇到麻烦,也许你可以将这个责任移到另一个依赖?这将允许您替换此依赖关系,并从此测试的角度精确控制这种情况。