“正当的做法”有多少嘲笑?


10

我开玩笑地为这个问题命名,因为我确定“取决于情况”,但是我有一些具体的问题。

在具有许多深层依赖项的软件中工作时,我的团队已经习惯了使用相当广泛的模拟将每个代码模块与其下的依赖项分开的习惯。

因此,令我惊讶的是Roy Osherove此视频中建议您仅在5%的时间内使用模拟。我猜我们坐在70-90%之间。我也时常看到其他类似的指导

我应该定义我认为是“集成测试”的两类,它们是如此不同,以至于它们实际上应该被赋予不同的名称:1)集成多个代码模块的进程内测试;以及2)进行对话的进程外测试。到数据库,文件系统,Web服务等。我关心的是类型#1,这些测试将所有代码模块都集成在进程中。

我阅读的许多社区指南都建议您应该使用大量独立的,细粒度的单元测试,以及少量的粗粒度的端到端集成测试,因为单元测试会为您提供有关确切位置的精确反馈可能已经创建了回归,但是设置繁琐的粗略测试实际上验证了系统的更多端到端功能。

鉴于此,似乎有必要经常使用模拟来隔离这些单独的代码单元。

给定一个对象模型,如下所示:

在此处输入图片说明

...还要考虑到我们应用程序的依赖深度比我在该图中适合的深度要深得多,因此在2-4层和5-13层之间有多层N。

如果我想测试在单元#1中做出的一些简单的逻辑决定,并且是否将每个依赖项都构造函数注入到依赖于它的代码模块中,例如将2、3和4构造函数注入到模块1中,图片,我宁愿将2、3和4的模拟物注入1。

否则,我将需要构造2、3和4的具体实例。这可能比仅仅进行一些额外的键入要困难得多。通常2、3和4会有构造器要求,要满足这些挑战可能很困难,根据图(并根据我们项目的实际情况),我将需要构造N到13的具体实例,以满足N的构造器。 2 3 3

当我需要2、3或4以某种方式运行时,这种情况变得更具挑战性,以便我可以测试#1中的简单逻辑决策。我可能需要一次理解整个对象图/树并“在逻辑上进行推理”,以使2、3或4以必要的方式运行。做myMockOfModule2.Setup(x => x.GoLeftOrRight())。Returns(new Right());通常看起来容易得多。当模块2告诉它正确时,测试模块1是否按预期响应。

如果我要一起测试2 ... N ... 13的具体实例,那么测试设置将非常大,并且几乎是重复的。测试失败可能无法很好地指出回归失败的位置。测试不会是独立的另一个支持链接)。

当然,对底层进行基于状态的测试,而不是基于交互的测试,通常是合理的,因为这些模块很少具有任何进一步的依赖性。但似乎从定义上讲,模拟几乎是必要的,以隔离最底层之上的任何模块。

考虑到所有这些,谁能告诉我我可能会缺少什么?我们的团队过度使用模拟吗?还是在典型的单元测试指南中可能存在一些假设,即大多数应用程序中的依赖层将足够浅,因此测试集成在一起的所有代码模块确实是合理的(使我们的案例“特殊”)?或者,也许是不同的,我们的团队是否没有适当地限制我们有限的环境?


在我看来,您的应用程序可以从宽松的耦合中受益。 en.wikipedia.org/wiki/Loose_coupling
罗伯特·哈维

1
Or is there perhaps some assumption in typical unit testing guidance that the layers of dependency in most applications will be shallow enough that it is indeed reasonable to test all of the code modules integrated together (making our case "special")? <-这个。
罗伯特·哈维

还值得注意的是:回归测试(尤其是集成测试)的目的是证明您的软件仍然可以运行,而不必确定它在哪里损坏。您可以通过故障排除来解决问题,解决问题,然后通过其他单元测试解决特定的故障。
罗伯特·哈维

我应该在原始帖子中更清楚地说,模块1仅知道I2,I3和I4。模块2仅知道I5,I6和I7。在不使用模拟的情况下进行测试只是可疑的目标,我会提供具体的2、3和4至1,从而导致我所描述的挑战。否则,我们最终会超过5%的时间使用模拟游戏。
ardave

在阅读有关许多团队违反有价值的约定的博客帖子后,我有点开玩笑说我们的案例是“特殊的”,因为他们错误地认为自己的处境是“特殊的”。但是,如果这确实是我们的情况,那么这将解释我阅读的一些社区指南与团队的一些实际经验之间的差异。(5%vs 70-90%)
发狂

Answers:


4

我们的团队过度使用模拟吗?

乍看之下。

如果您具有1..13模块,则每个模块都应具有自己的单元测试,并且所有(非平凡,不受信任的)依赖项都应替换为测试版本。那可能意味着嘲笑,但是有些人对命名很痴迷,所以假货,填充物,空对象……有些人将所有测试实现称为“嘲笑”。这可能是关于“正确”多少的困惑的根源。

就我个人而言,我只是将所有测试对象称为“模拟对象”,因为区分它们的种类通常并不有用。只要它们使我的单元测试保持快速,独立和有弹性...我不在乎它们的名字。


我想知道那时是否有任何通用指南,相​​对于测试集成在一起的多个代码模块,什么时候最好进行隔离测试。似乎只要我集成了本来可以孤立的任何两个模块,我就会面临很多不良问题:缺乏精确的回归原因/多次测试因一次回归而失败,过多的测试设置等。我有自己的直觉(“听听测试”),但这使我处于70-90%的模拟水平。
ardave

1
@nono-根据我的经验,出于提及的原因,您应该单独测试所有内容。您在隔离不单元测试的唯一的东西是东西,你不能因为他们违背了一些文件系统或其他直接外部资源(已做毕竟)。
Telastyn

经过几天的咀嚼,您似乎是最好的解释:如果使用严格的“模拟”定义作为用于追溯交互/行为验证的测试对偶类型,而不是虚拟测试对偶或一个预先配置为模拟某些行为的测试倍数,然后是的,我可以看到以5%的水平结束。
ardave
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.