Arg.Do()没有在预期的时候触发.Do for void方法

本文关键字:Do 候触发 void 方法 for Arg | 更新日期: 2023-09-27 18:09:53

在我的测试中有以下结构,旨在测试使用正确的复杂参数对象调用某个日志,即使它抛出异常,然后包装并通常进一步操作。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<>(),并且它确实有对logThing的单个调用的记录,并带有正确的参数-这只是Arg。Do似乎只在When..Do块完成后执行。很明显,这意味着既然我要把时间…做,它永远不会到达它。

我真的没有期望NSubstitute以这种方式排序调用,这是预期的行为吗?如果是这样,我能做些什么来测试传入的参数像这样,或者我应该把我的参数检查到主When. do块(这使得它更难阅读)?

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

Arg.Do()没有在预期的时候触发.Do for void方法

When..Do和Arg.的确切顺序。Do没有定义,我不建议依赖它,因为我认为它可能在不同版本之间根据实现而改变。(如果你有一个很好的理由以一个特定的顺序定义它,请将你的建议发布到用户组。)

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

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

如果你需要更复杂的比较逻辑,你可以做同样的事情,但使用一个方法来检查参数:

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

CheckLogEntry可以做任何你需要的检查,并且你可以像以前一样保留When.. do throw。

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

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

关于其他测试方法的建议,我不完全清楚您在这里要做什么,但是如果您在隔离包装输出来自哪里时遇到麻烦,也许您可以将此责任转移到另一个依赖项?这将允许您替换这个依赖项,并从测试的角度精确地控制发生这种情况的位置。