为啥使用集成测试而不是单元测试是一个坏主意?

Posted

技术标签:

【中文标题】为啥使用集成测试而不是单元测试是一个坏主意?【英文标题】:Why using Integration tests instead of unit tests is a bad idea?为什么使用集成测试而不是单元测试是一个坏主意? 【发布时间】:2010-12-19 19:03:51 【问题描述】:

让我从定义开始:

单元测试是一种软件验证和确认方法,程序员可以在其中测试各个源代码单元是否适合使用

集成测试是一种软件测试活动,其中将各个软件模块组合在一起并作为一个组进行测试。

尽管它们经常用于不同的目的,但这些术语经常混淆。开发人员将自动化集成测试称为单元测试。还有一些人争论哪个更好,这在我看来是一个错误的问题。

我想请开发社区分享他们对为什么自动化集成测试不能取代经典单元测试的看法。

以下是我自己的观察:

    集成测试不能与 TDD 方法一起使用 集成测试很慢,不能经常执行 在大多数情况下,集成测试并不能指出问题的根源 使用集成测试创建测试环境更加困难 确保高覆盖率更加困难(例如模拟特殊情况、意外故障等) 集成测试不能与Interaction based testing 一起使用 Integration tests move moment of discovering defect further(来自paxdiablo)

编辑:再次澄清一下:问题不在于是使用集成还是单元测试,而不是哪个更有用。基本上,我想向只编写集成测试并将它们视为单元测试的开发团队收集论据。 任何涉及来自不同层的组件的测试都被视为集成测试。这是为了与以隔离为主要目标的单元测试进行比较。

谢谢, 安德烈

【问题讨论】:

您应该将其拆分为单独的问题和答案,而不是在您的问题中回答。我也会制作这个社区 wiki,因为没有一个正确的答案——它更加主观和以讨论为导向。 另一方面,如果所有单元测试都正常工作,并不意味着应用程序可以正常工作。代码和单元测试中的假设可能是错误的。这就是为什么我认为集成和单元测试是互补的。 鉴于编辑,我认为您在这里提出了错误的问题。您似乎想要的是更接近“[真正的]单元测试提供的价值是集成测试不提供的什么?”。正如extraneon 指出的那样,还有一个问题的倒置版本。 请注意,这有点(尽管不完全)是错误的二分法:例如除了单元和集成之外,我们还使用 FIT 测试。 我在使用 TDD 时已经编写了数以千计的 集成 测试,所以您的第一次观察可能是基于一些误解。此外,虽然集成测试可能很慢,但它们也可能很快;这取决于几个因素。 【参考方案1】:

有研究(a)表明,随着您远离引入错误的地方,修复错误的成本会变得更高。

例如,修复软件中的错误通常花费相对较少,而您甚至还没有升级到源代码控制。这是你的时间,而且不多,我保证(假设你擅长你的工作)。

相比之下,当客户(或您的所有客户)发现该问题时,解决该问题的成本是多少。多层次的人参与其中,新软件必须快速构建并推向现场。

这是极端的比较。但即使是单元测试和集成测试之间的差异也是显而易见的。单元测试失败的代码主要只影响单个开发人员(当然,除非其他开发人员/测试人员/等正在等待它)。但是,一旦您的代码参与了集成测试,缺陷就会开始阻碍您团队中的其他人。

我们不会梦想用集成测试代替我们的单元测试,因为:

我们的单元测试也是自动化的,因此除了初始设置之外,运行它们的成本很低。 它们构成了集成测试的开始。所有单元测试都在集成阶段重新运行,以检查集成本身没有破坏任何东西,然后集成团队已经添加了额外的测试。

(a) 例如,参见http://slideshare.net/Vamsipothuri/defect-prevention,幻灯片#5,或在网上搜索Defect prevention : Reducing costs and enhancing quality。图表中的图表复制如下,以防在网上很难找到:

【讨论】:

非常困惑的答案,IMO。没有解释“修复错误的成本”与“使用集成测试而不是单元测试”之间的关系。并且似乎将集成测试视为来自多个开发人员的代码的集成,这是由团队的VCS负责的单独步骤;对我来说,在将更改提交到共享代码存储库之前以及执行任何必要的合并之后会运行一个新的集成测试;而且大多数项目没有集成阶段,也没有集成团队 @Rogério,“集成测试”一词在问题本身中的定义非常明确:“将单个软件模块组合在一起并作为一个组进行测试”。这是我使用的,就我而言,它也是规范的定义。至于所谓的错误修复成本的缺失应用程序,我认为我已经说清楚了,但我会看看我是否可以改写:你应该执行单元测试,而不是只是 集成测试,因为在 UT 阶段修复错误更容易/更便宜/省力。 而且,对不起,尽我所能,我无法找到一种方法让答案更清晰,所以,如果你对此有任何想法,我愿意接受。否则,我只能建议您根据我的评论重新阅读问题/答案。 好吧,更具体地说,令人困惑的部分是它说“如果你的代码在集成中失败,你就会开始阻止你所有的队友......”。嗯,怎么会?在这方面,失败的集成测试与失败的单元测试没有什么不同:在任何一种情况下,它都不会持有其他任何人。除非您指的是其他类型的集成,否则需要更多信息。关于“修复错误的成本”,我仍然没有明确解释单元测试如何/为什么更便宜。但是好吧,假设它单元测试更便宜。所以呢?这本身不足以声称他们值得付出代价。 集成测试中的“集成”只是通过在同一个测试中执行多个类(或“单元”)来实现,而不是为每个类进行单独的单元测试;而已。或者,用 OP 的话来说:“任何涉及来自不同层的组件的测试都被视为集成测试。这是为了与以隔离为主要目标的单元测试进行比较。”【参考方案2】:

(我认为)OP 在这里所说的集成测试更倾向于场景级别的测试。

但是我们在哪里划清 unit -> integration -> scenario 之间的界限呢?

我经常看到开发人员编写一个功能,然后在对其进行单元测试时嘲笑该功能使用/消耗的所有其他代码,只测试他们自己的功能代码,因为他们认为其他人测试过,所以应该没问题.这有助于代码覆盖,但通常会损害应用程序。

理论上 Unit 测试的小隔离应该涵盖很多,因为所有内容都在其自己的范围内进行测试。但是这样的测试是有缺陷的,并没有看到完整的画面。

一个好的单元测试应该尽量少模拟。例如,模拟 API 和持久性。即使应用程序本身不使用 IOC(控制反转),如果每个从事该项目的开发人员都这样做,它也应该很容易启动一些对象进行测试而无需模拟,那么它会变得更加容易。 那么测试很有用。这类测试具有集成特性,编写起来并不容易,但可以帮助您发现代码的设计缺陷。如果不容易测试,那么调整您的代码以使其易于测试。 (TDD)

优点

    快速识别问题 甚至在 PR 合并之前提供帮助 易于实施和维护 提供大量数据用于代码质量检查(例如覆盖率等) 允许 TDD(测试驱动开发)

缺点

    错过场景集成错误 在自己的代码中屈服于开发人员的盲目性(我们所有人都会发生)

一个良好的集成测试将针对完整的端到端场景执行,甚至检查单元测试无法涵盖的持久性和 API,以便您知道在失败时首先查看的位置。 p>

优点:

    测试接近真实的 e2e 场景 发现开发人员没有考虑过的问题 对微服务架构非常有帮助

缺点:

    大部分时间都很慢 通常需要相当复杂的设置 环境(持久性和 api)污染问题(需要清理步骤) 在 PR(Pull 请求)上使用大多数情况下是不可行的

TLDR:你需要两者,你不能用另一个代替!问题是如何设计这样的测试以充分利用两者。而不仅仅是让他们向管理层展示良好的统计数据。

【讨论】:

【参考方案3】:

我如何看待集成测试和单元测试:

单元测试:使用低级细节单独测试小事情,包括但不限于“方法条件”、检查、循环、默认设置、计算等。

集成测试:测试范围更广,涉及到组件的数量,这会影响结合在一起时其他事物的行为。集成测试应涵盖端到端集成和行为。集成测试的目的应该是证明系统/组件在集成在一起时可以正常工作。

【讨论】:

【参考方案4】:

“使用集成测试而不是单元测试”是个坏主意,因为这意味着您没有意识到他们正在测试不同的东西,当然通过和失败的测试会给你不同的信息。当他们从任何一方接近它时,它们构成了一种阴阳测试。

集成测试采用模拟用户如何与应用程序交互的方法。这些将减少对手动测试的需求,并且通过测试可以告诉您您的应用程序可以在多个平台上运行。失败的测试会告诉您有问题,但通常不会为您提供有关底层代码有什么问题的大量信息。

单元测试应侧重于确保函数的输入和输出在所有情况下都是您期望的。通过单元测试可能意味着您的函数按照规范工作(假设您对所有情况都有测试)。但是,您的所有功能在隔离中正常运行并不一定意味着在部署时一切都会完美运行。失败的单元测试将为您提供有关失败原因的详细、具体的信息,这在理论上应该更容易调试。

最后,我相信单元测试和集成测试的结合将产生最快且最无错误的软件。您可以选择使用一个而不是另一个,但我避免使用“代替”这个短语。

【讨论】:

【参考方案5】:

编写单元测试是为了测试类上的方法。如果该类依赖于任何类型的外部资源或行为,您应该模拟它们,以确保您只测试您的单个类。单元测试中不应有外部资源。

集成测试是更高级别的粒度,正如您所说,您应该测试多个组件以检查它们是否按预期协同工作。大多数项目都需要集成测试和单元测试。但重要的是它们要分开并理解差异。

在我看来,单元测试对于人们来说更难掌握。它需要对 OO 原则有很好的了解(基本上基于一类责任)。如果您能够单独测试所有类,那么您就有可能拥有一个设计良好、可维护、灵活且可扩展的解决方案。

当您签入时,您的构建服务器应该只运行单元测试和 它们应该在几秒钟内完成,而不是几分钟或几小时。 集成测试应在夜间运行或根据需要手动运行。

【讨论】:

【参考方案6】:

集成测试告诉您它是否有效。单元测试告诉你什么是行不通的。只要一切正常,您“不需要”单元测试——但是一旦出现问题,让单元测试直接指出问题是非常好的。正如您所说,它们有不同的用途;两者兼得很好。

直接解决您的主题:集成测试不是问题,不是问题。使用它们而不是单元测试是。

【讨论】:

如果您在 Google 图片中搜索“测试金字塔”,值得注意的是每个人都有自己的版本,但它们之间却有着惊人的哲学一致性。【参考方案7】:

这两种类型的测试是不同的。在我看来,单元测试不能替代集成测试。主要是因为集成测试通常是特定于上下文的。您可能会遇到单元测试失败而您的集成没有失败的情况,反之亦然。如果您在一个使用许多其他组件的类中实现了不正确的业务逻辑,您会希望您的集成测试突出这些,您的单元测试会忽略这一点。我知道集成测试既快速又简单。我认为你每次对代码库进行更改时都依赖于你的单元测试,并且拥有一个绿色列表会让你更有信心,你没有破坏单个类级别的任何预期行为。单元测试为您提供了针对单个类的测试,该类正在执行其设计目的。集成测试测试多个类协同工作是否按照您期望它们为特定协作实例执行的操作。这就是 OO 开发的全部理念:封装特定逻辑的单个类,允许重用。

【讨论】:

【参考方案8】:

我发现集成测试明显优于单元测试。如果我对我的代码进行单元测试,我只是在测试它的作用与我对它应该做什么的理解。这只捕获实现错误。但通常更大的问题是理解错误。集成测试两者兼得。

此外,还有巨大的成本差异;如果您正在大量使用单元测试,那么它们超过所有其他代码放在一起的情况并不少见。并且它们需要维护,就像代码的其余部分一样。集成测试要便宜得多——而且在大多数情况下,无论如何您都已经需要它们了。

在极少数情况下可能需要使用单元测试,例如对于如果系统的其余部分正常工作则无法触发的内部错误处理路径,但大多数情况下,单独的集成测试可以以更低的成本提供更好的结果。

【讨论】:

【参考方案9】:

一切都是为了减少迭代时间。

通过单元测试,您可以编写一行代码并在一分钟左右的时间内对其进行验证。对于集成测试,通常需要更长的时间(并且成本会随着项目的增长而增加)。

两者显然都很有用,因为两者都能检测到对方无法检测到的问题。

OTOH,从“纯”TDD 方法来看,单元测试不是测试,它们是功能规范。 OTOH,集成测试确实是在更传统的意义上进行“测试”。

【讨论】:

单元测试肯定测试,即使它们是经典 TDD 中的功能规范?因为他们(附加地,但并非巧合地)验证测试代码在对代码进行任何后续更改(后来以及出于其他原因)之后继续以指定的方式运行。【参考方案10】:

集成测试可让您检查应用程序的整个用例是否正常工作。

单元测试检查应用程序中的低级逻辑是否正确。

集成测试更有助于管理人员对项目状态感到更安全(但对开发人员也很有用!)。

单元测试对于开发人员编写和更改应用程序逻辑更有用。

当然,同时使用它们以获得最佳效果。

【讨论】:

【参考方案11】:

在许多情况下,您需要两者。就我对使用集成测试作为单元测试而言,您的观察是正确的,但它们并不意味着集成测试没有价值或不需要,只是它们服务于不同的目的。有人同样可以争辩说,单元测试不能取代集成测试,正是因为它们消除了对象之间的依赖关系,并且它们不锻炼真实环境。两者都是正确的。

【讨论】:

【参考方案12】: 单元测试专注于测试单个组件,而不依赖于外部依赖项。它们通常与模拟或存根一起使用。 集成测试涉及多个组件,并且可能依赖于外部依赖项。

我认为两者都很有价值,在他们所做的工作中,任何一方都无法替代另一方。我确实看到很多集成测试伪装成单元测试,尽管它们存在依赖关系并且需要很长时间才能运行。它们应该单独运行,并作为持续集成系统的一部分。

集成测试确实经常发现单元测试没有的东西......

【讨论】:

【参考方案13】:

我认为覆盖是主要问题。

一个特定的小组件(例如方法或最多一个类)的单元测试应该在每个法律场景中测试该组件(当然,一个抽象等价类,但应该涵盖每个主要的类)。因此,此时应捕获违反既定规范的更改。

在大多数情况下,集成仅使用每个子单元可能场景的子集,因此故障单元仍有可能生成最初集成良好的程序。

由于您在下面指定的所有原因,通常很难在集成测试中实现最大覆盖率。如果没有单元测试,对本质上在新场景中运行的单元所做的更改更有可能不会被捕获,并且可能在集成测试中被遗漏。即使没有遗漏,也很难确定问题所在。

我不确定大多数开发人员是否将单元测试称为集成测试。我的印象是,大多数开发人员都了解这些差异,这并不意味着他们也实践过。

【讨论】:

【参考方案14】: 集成测试很慢。 集成测试可能会破坏不同的 原因(它没有重点和 孤立)。因此你需要更多 调试失败。 组合 场景对于集成来说太大了 在未进行单元测试时进行测试。

我主要进行单元测试和 10 倍的集成测试(配置、查询)。

【讨论】:

【参考方案15】:

集成测试通常发生在单元测试之后。我不确定测试本身未经过测试的单元之间的交互有什么价值。

如果齿轮可能会损坏,那么测试机器的齿轮如何一起转动是没有意义的。

【讨论】:

以上是关于为啥使用集成测试而不是单元测试是一个坏主意?的主要内容,如果未能解决你的问题,请参考以下文章

单元测试集成测试

单元测试集成测试

单元测试/集成测试/系统测试的区别

《Google软件测试之道》

java单元/集成测试中使用Testcontainers

java单元/集成测试中使用Testcontainers