在单元测试中验证函数调用顺序

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。正确的输出或一些异常。不要测试内部结构。这会导致脆弱的单元测试。

以上是关于在单元测试中验证函数调用顺序的主要内容,如果未能解决你的问题,请参考以下文章

什么是单元测试?如何做好单元测试?

springboot 怎么单元测试

springboot怎么 进行单元测试

Python——单元测试中mock原理和使用

idea springboot里面怎么单元测试

Unity游戏开发浅谈Unity游戏开发中的单元测试