TDD模拟呼叫验证-是反模式吗?


11

我已经进行了一年的TDD测试,对此我感觉很好,我喜欢我的测试套件以及所有其他工具。但是,我注意到最近我一直在做很多模拟通话验证。例如,我有一个将注入存储库的服务-在我的单元测试中,我将传递一个存储库的模拟并验证它在我正在测试的方法中被调用。然后,我将检查返回的结果是否正确(在另一个测试中)。这绝对是“感觉”错误,因为我的单元测试现在与实现细节非常相关。我听说您应该测试“行为”,但是在许多情况下……嗯-不可能吗?如果你有一个void例如,您通常会测试副作用。我的意思是继续进行并展示一些简单的代码集很容易就能证明这一点,但是恕我直言,它不能很好地反映我们编写的真实程序。我做错了吗?这种测试是一种反模式吗?感谢您对此的意见,在TDD方面,我还是一个新手。


2
简短的回答:是的。关于此主题的非常有趣的问题已经在此处出现。您的单元测试不应脆弱,并且在很大程度上取决于您的实现。这就是为什么要进行更高级别的测试(集成等)的原因。在这里:programmers.stackexchange.com/questions/198453/...
Kemoda

@Kemoda,如果您可以将我链接至讨论或其他进一步的材料,我将不胜感激,我非常想提高自己的技术。
Dimitar Dimitrov

1
你有这个例子,例如programms.stackexchange.com/questions/198453/……我稍后会找到其他链接
Kemoda 2013年

Answers:


8

好吧,您应该尝试测试输入和输出。您应该验证外部可见的行为。课堂上的“承诺”或“合同”。

同时,有时没有一种方法比做您所说的更好。

我确实认为这会使您的测试变得更加脆弱,因此,如果可以的话,您应该避免依赖于实现细节的测试,但这不是一帆风顺的事情。有时候没关系,最糟糕的事情是您更改实现并必须更新测试。


2

测试的目的是限制可能的生产实现。确保只对实际需要的实施施加限制。通常,这是什么你的程序应该做的,而不是如何它它。

因此,例如,如果您的服务向存储库中添加了某些内容,则应在此之后测试新条目是否包含在存储库中,而不是触发添加操作。

为此,您需要能够在服务测试中使用存储库实现(在其他地方测试)。我发现使用协作者的实际实现通常是一种好方法-因为它实际上是最好的实现。


“所以,如果在测试中使用真实的实现会很昂贵(例如,因为它们需要设置复杂的资源)怎么办?在这种情况下,我需要使用模拟,对吗?”

无论如何,您可能都希望进行一次集成测试,以测试实际的实现方式是否可以协同工作。确保完成一项集成测试即可测试您的服务。或换句话说:如果服务将许多协作者连接在一起(因此可能很难测试),请确保该服务不包含任何逻辑。如果是这样,并且您需要进行多个(集成)测试,则需要更改代码的结构,例如,通过隔离逻辑,使其更具可测试性。

在这种情况下使用模拟减轻了测试不良隔离逻辑的痛苦,因此隐藏了体系结构问题。因此,请勿使用模拟来测试结构不良的代码,而应修复结构。


1
我明白你在说什么。关于“多少测试就是太多测试”,这个话题有点令人困惑,比方说,我有一个“聚合服务”,它基本上是一个外观,只是将其他服务/存储库/组件“胶合”在一起,进行什么样的测试你为此写吗?我所能想到的就是电话验证。我希望我有道理。
Dimitar Dimitrov

2

我的想法是:“综合服务”。

通话验证可以做到这一点,但不会带来太多价值。您只是在检查接线。

有3种非排他性的其他方式:

  1. 扩展对每个服务的测试,以检查更高级别的行为。例如,如果您在服务的单元测试中命中内存数据库,则将其升级,以便针对实际的数据库测试服务。服务层位于抽象树的更高层,因此您的测试也应如此。

  2. 使用代码生成直接从聚合服务创建服务。

  3. 使用某种反思或动态语言来做同样的事情。例如,在Java中,可以使用Groovy接口,该接口直接将调用继续传递。

可能还有其他方法可以执行此操作,但是仅检查接线的回报率就很低,这会使您难以接受该实现。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.