我在我的测试中有以下结构,旨在测试使用正确的复杂参数对象调用某个日志,即使它抛出异常然后被包装并进一步操作。 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正在接收我期望的内容。不过,如果有更好的方法,我肯定愿意接受建议。
答案 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");
});
就其他测试方法的建议而言,我并不完全清楚你要在这里做什么,但如果你在隔离包装输出的来源方面遇到麻烦,也许你可以将这个责任移到另一个依赖?这将允许您替换此依赖关系,并从此测试的角度精确控制这种情况。