在单元测试中验证函数调用顺序
Posted
技术标签:
【中文标题】在单元测试中验证函数调用顺序【英文标题】:Verifying function call order in a unit test 【发布时间】:2013-04-11 17:08:11 【问题描述】:我想要一个单元测试来验证 2 个函数调用以正确的顺序发生。在示例中,第一个函数加密文件并将其保存到文件系统,第二个函数将加密文件发送到第 3 方处理器(通过 FTP)。
我使用 NSubstitute 作为模拟框架和 FluentAssertions 来帮助进行测试验证。使用开箱即用的 NSubstitute 似乎无法实现这一点。
public void SendUploadToProcessor(Stream stream, string filename)
var encryptedFilename = FilenameBuilder.BuildEncryptedFilename(filename);
FileEncrypter.Encrypt(stream, filename, encryptedFilename);
FileTransferProxy.SendUpload(encryptedFilename);
[TestMethod, TestCategory("BVT")]
public void TheEncryptedFileIsSent()
var stream = new MemoryStream();
var filename = Fixture.Create<string>();
var encryptedFilename = Fixture.Create<string>();
FilenameBuilder
.BuildEncryptedFilename(Arg.Any<string>())
.Returns(encryptedFilename);
Sut.SendUploadToProcessor(stream, filename);
// Something here to verify FileEncrypter.Encrypt() gets called first
FileTransferProxy
.Received()
.SendUpload(encryptedFilename);
【问题讨论】:
【参考方案1】:在NSubstitute.Experimental
命名空间中尝试Received.InOrder
。
类似这样的东西(我还没有测试过):
Received.InOrder(() =>
FileEncrypter.Encrypt(stream, filename, encryptedFilename);
FileTransferProxy.SendUpload(encryptedFilename);
);
如果您不习惯依赖实验性功能,则需要设置回调以按顺序存储调用,然后对其进行断言。
var calls = new List<string>(); //or an enum for different calls
FileEncrypter.When(x => x.Encrypt(stream, filename, encryptedFilename))
.Do(x => calls.Add("encrypt"));
FileTransferProxy.When(x => x.SendUpload(encryptedFilename))
.Do(x => calls.Add("upload"));
// Act
// Assert calls contains "encrypt","upload" in the correct order
如果您最终尝试了Received.InOrder
,请在discussion group 上留下一些反馈。如果我们得到一些关于它对其他人运作良好的反馈,那么我们可以将它提升到核心命名空间。
【讨论】:
谢谢大卫。我忘记了 When-Do 结构,这就是我要找的东西。 NSubstitute.Experimental 命名空间在 NuGet 包中不可用,如何获得包含该命名空间的 NSubstitute 版本? NuGet 版本有它(它不是单独的 DLL,只需使用using NSubstitute.Experimental
)。它是在 v1.5.0 中引入的(当前版本是 v1.6.0)。
我最初尝试放入 using 语句,但由于某种原因无法识别。它现在正在工作。我在讨论组中留下了一些反馈。谢谢!【参考方案2】:
虽然这不是一个持久的答案,但将显式顺序验证为单元测试的一部分是不好的做法。你永远不应该测试实现细节。只需确保将输入正确转换为输出并添加一些替代方案,这些方案基本上可以证明预期的行为。这正是这个功能在 RhinoMocks 中被弃用并且 FakeItEasy 甚至不支持它的确切原因。
【讨论】:
FakeItEasy 支持有序断言:github.com/FakeItEasy/FakeItEasy/wiki/Ordered-assertions。我同意应尽可能避免使用它,但在某些情况下它可能很有用。 实用单元测试这本书包括时间(事情发生的顺序),因为它是一个完全有效的规范,例如“在调用保存之前需要调用一些验证器。”或者,在这种情况下,“需要在上传之前调用加密器。”。您还如何确保在其依赖项上调用 void 方法的方法中实现这种规范? 确认上传的结果是否经过加密不是更好的测试吗? 我同意@RyanTheLeach。您只能断言可观察到的结果。 IE。正确的输出或一些异常。不要测试内部结构。这会导致脆弱的单元测试。以上是关于在单元测试中验证函数调用顺序的主要内容,如果未能解决你的问题,请参考以下文章