你应该只模仿你拥有的类型吗?

Ode*_*ded 39 mocking

我通过TDD阅读:只有 Mark Needham 拥有的模拟类型,并且想知道这是否是最佳实践?

请注意,他不是反对嘲笑,而是反对直接嘲笑 - 他确实说写一个包装并嘲笑这很好.

Rog*_*rio 31

我的回答是"不".你应该在给定单元测试的上下文中模拟任何有意义的东西.如果你"拥有"嘲笑类型也没关系.

现在,在Java或.NET环境中,一切(我真的意味着一切)都可以很容易地被嘲笑.因此,没有技术理由去首先编写额外的包装代码.


我最近(2010年11月)一直在思考的其他一些想法,表明"只有你自己的模拟类型"的不合逻辑可能是:

  1. 假设您确实为第三方API创建了一个包装器,然后在单元测试中模拟包装器.但是,稍后您认为包装器可以在另一个应用程序中重用,因此您将其移动到单独的库中.所以现在包装器不再由你"拥有"(因为它被用在多个应用程序中,可能由不同的团队维护).开发人员应该为旧的包装器创建一个新的包装器吗?并继续递归,添加一层层基本无用的代码?
  2. 假设其他人已经为一些非平凡的API创建了一个很好的包装器,并使其可用作可重用的库.如果所说的包装器正是我需要的特定用例,我应该首先为包装器创建一个包装器,使用几乎相同的API,这样我就会"拥有"它吗?!?

有关具体而实际的示例,请考虑Apache Commons Email API,它只不过是标准Java Mail API的包装器.由于我不拥有它,每当我为需要发送电子邮件的类编写单元测试时,我是否应该始终为Commons Email API创建一个包装器?

  • 对不起,但我不知道为什么这比 /sf/answers/2235700001/ 更受欢迎,这个答案解释得很好。规则是有道理的。您拥有的库并不意味着定义在同一个包中。您知道何时为您创建的库更改了实现。即使它是一个开源库,并且如果您知道它何时发生变化,也可以继续模拟它。但是当您不知道实现何时发生变化时,这会变得困难。见鬼,有时 api 可能会改变,这就是我们首先使用包装器的原因。 (2认同)

aku*_*uhn 24

取决于你是否意味着模拟或模拟™...

鉴于您只是使用模拟框架(例如Mockito)来创建存根,那么创建您不拥有的类型的存根是完全可以合理的.

但是,如果您使用模拟框架(例如Mockito)来创建mock™对象,那么您最好遵循mock™传播者的建议.就个人而言,我对这一运动失去了联系,所以我无法告诉你Mark Needham的建议是否被认为是犹太人.

除此之外,马克写的关于EntityManagers在Hibernate中嘲笑的内容听起来本身就是合理的.但是我怀疑我们可以从这个特定的案例中概括出一个像"从不模拟你不拥有的类型"这样的规则.有时它可能有意义,有时不是.

  • 您能澄清一下模拟和模拟™之间的区别吗?我知道已经过去十多年了(!),但它似乎仍然相关。 (5认同)
  • 我认为他使用了这两个术语,因为在mockito中“mock”也是一个存根 (3认同)

H6.*_*H6. 15

我喜欢Mockito项目对这个问题的解释.

不要嘲笑你不拥有的类型!

这不是一个强硬路线,但越过这条线可能会产生影响!(很可能会)

  1. 想象一下嘲笑第三方lib的代码.在第三个库的特定升级之后,逻辑可能会改变一点,但测试套件将执行得很好,因为它被嘲笑.所以后来,认为一切都很好,毕竟构建墙是绿色的,软件部署和......轰隆隆
  2. 这可能表明当前设计与第三方库没有足够的分离.
  3. 另一个问题是第三方库可能很复杂,需要大量的模拟才能正常工作.这导致过度指定的测试和复杂的固定装置,这本身就损害了紧凑和可读的目标.或者由于模拟外部系统的复杂性而导致的测试不足以覆盖代码.

相反,最常见的方法是围绕外部lib /系统创建包装器,尽管应该意识到抽象泄漏的风险,其中过多的低级API,概念或异常超出了包装器的边界.为了验证与第三方库的集成,编写集成测试,并使它们尽可能紧凑和可读.

  • 我不喜欢这个答案.单元测试应测试您编写的最小工作单元.他们应该将自己与第三方依赖关系隔离开来.使用Integration测试来改变这些问题.我不希望来自第三方库的HTTP调用和数据库调用减慢我的单元测试速度. (7认同)
  • 我完全不明白你引用@H6的第一点。我确实同意随着第 3 方库的升级,测试将通过,但他们建议作为替代方案是什么?在第 3 方库上创建一个包装器并模拟它。那么这里的逻辑是什么呢?我的意思是,在他们提出的解决方案中,我们仍然会遇到同样的问题,测试不会发现第 3 方更改,因为我们模拟了调用它们的包装器,所以除了我们刚刚创建了一个不执行此操作的额外函数之外,还有什么区别没有什么? (3认同)
  • 单元测试都是关于隔离的。我不认为嘲笑是一个问题,只要你在你的集成测试中有责任。为了测试而进行包装会引入其自身的复杂性:除非它们是微不足道的,否则在某些时候您的包装器也需要进行测试。 (2认同)
  • 就其价值而言,我认为这应该是公认的答案。 (2认同)

cle*_*tus 9

我打算说"不",但快速浏览一下博文,我可以看到他的内容.

他特别谈到了在Hibernate中嘲笑EntityManagers.我反对这个.EntityManagers应该隐藏在DAO(或类似的)中,DAO应该被嘲笑.测试一行调用EntityManager完全浪费你的时间,一旦发生任何变化就会中断.

但是,如果你确实有第三方代码,你想测试你如何与它进行交互.