【问题标题】:Is unit testing needed in addition to integration testing of DB, filesystem, etc.?除了数据库、文件系统等的集成测试之外,还需要单元测试吗?
【发布时间】:2013-03-15 20:02:55
【问题描述】:

在我的办公室,除了对主要负责与文件系统(DB 等)交互的类进行集成测试外,我们对单元测试的必要性存在争议。

我们拥有的集成测试几乎是单元测试,因为测试对象根本不与其他对象交互。我们将测试称为 integration 的唯一原因是在测试中使用了真正的文件系统。并且建议让被测类使用文件系统层组件,然后在测试中模拟它(所以我们将它们称为unit测试),并检查与该组件的交互,而不是真实的文件系统结果。我们讨论的是这种改变的必要性。


我们的一个观点是,单元测试总是需要的,因为:

  1. 编写单元测试让代码变得更好
  2. 拥有单元测试,您无需关心真实的文件系统和文件的副作用,出现在错误的位置
  3. 开发人员可以通过使测试的类使用文件系统模拟并为该模拟设置适当的期望来全面测试结果
  4. 可以将模拟期望与测试类的特定内部算法联系起来,因为我们使用单元测试进行白盒测试

因此,必须始终为这样的测试类编写单元测试。并且该类必须始终使用文件系统层组件进行测试。


另一种观点是,专门用于文件系统交互的类的特定边缘情况不需要单元测试,因为:

  1. 仅通过简单的模拟而不是真实的文件系统(或其完整仿真),无法正确验证测试类是否有效。文件系统是一个如此复杂的组件,它:
    • 经过测试的课程可以以多种不同的方式工作,以获得成功的结果。模拟期望仅涵盖一两种可能的场景,因此单元测试错误地显示正确实现良好算法的类的失败,这与预期不同。
    • 经过测试的类可以以某种方式工作,被模拟检测为成功的场景,但该类仍然没有产生正确的结果。这可能是由于实际文件系统中相当复杂的原因。所有这些原因是不可能仅仅通过一个模拟来涵盖的。
  2. 带有模拟和期望的单元测试非常脆弱,因为它与测试类的内部算法密切相关。并且即使对算法进行正确的更改,测试也会错误地失败。
  3. 当类只有 1-2 个公共方法,并且唯一的依赖项是文件系统时,集成测试是单元测试的正确和完全替代。集成测试在这种情况下提供与单元测试相同的好处 - 清晰的依赖关系,更易读的代码等。

因此,在我们的案例中不需要使用文件系统模拟进行单元测试。对于这种特殊的类情况,它很脆弱并且不准确。


所以,总而言之,问题是:
对于没有复杂类的边缘情况,集成测试是否足够充分,它主要负责使用文件系统(数据库等)?

这个类的 integrationunit 测试之间的唯一区别是,对于单元测试,将使用文件系统模拟(类将被完全隔离),而对于集成测试使用的真实文件系统。

如果您可以添加对经典书籍的引用,或者可能是知名业内人士的文章/演示文稿,我将不胜感激,这样我们就可以有一个非常强大的基础来支持得出的结论。

【问题讨论】:

  • growing-object-oriented-software.com 这是我参考的 TDD 书籍之一,也许这会有所帮助。顺便说一句,单元测试不需要测试每个分支,而是专注于测试行为。
  • stackoverflow.com/questions/129036/… 这是与此类似的关于 SO 的问题之一。
  • 谢谢你,@NarendraPathai。我看过那张票,但我也在寻找一些对普遍采用的来源(书籍、文章等)的参考。

标签: unit-testing mocking integration-testing


【解决方案1】:

最好有一个已知的文件系统或数据库状态用于测试。例如,您不希望测试失败,因为它试图插入已经存在的记录。此故障不是由于代码而是数据库问题。同样的事情也可能发生在文件系统中。但是,您应该编写您能够编写的最佳测试。如果您不能轻松地模拟文件系统或其他任何东西,请与它进行交互。只要意识到如果测试失败,它可能不是代码问题。

一个丑陋的测试总比没有测试好。 --Testivus之道 http://www.artima.com/weblogs/viewpost.jsp?thread=203994

现在,即使您确实使用 mock 进行了测试,但这并不意味着您不应该进行 QA 或某种集成测试来确保一切都正确连接。我认为单元测试用于验证代码的内部是否正常工作,而集成测试告诉我所有部分都可以协同工作。

我不知道您使用的是什么语言,但 PHPUnit 的文档提供了一些关于测试数据库和文件系统的想法。

带有模拟和期望的单元测试非常脆弱,因为它是 与测试类的内部算法非常相关。和测试 即使对算法进行正确的更改,也会错误地失败。

对于使用模拟进行测试,您不应该与算法绑定。您要测试的只是该类的预期行为。不是它是怎么做的。

【讨论】:

  • 谢谢,@Schleis。我听到你了。关于不绑定算法 - 我们的经验是文件系统模拟是不可能的。文件系统是一个复杂的组件,因此在事先不知道算法的情况下,无法正确地对模拟进行期望,并期望诸如“位置 /foo/bar 的文件已打开,某些特定内容写入此文件,文件已关闭” , 文件被复制到 /baz/bar 等”。而这种脆弱和不完整的期望是我们主要关心的问题。
【解决方案2】:

这里的简短回答是肯定的,您可以使用“集成”测试来全面测试一个类。不过,更好的问题是应该你这样做吗?

我认为您对“单元测试”(无外部依赖项)和“集成测试”(具有此类依赖项)之间的定义差异过于关注。测试的目标是让您确信您的代码始终在工作,同时降低拥有这种信心的相关成本。所以你的问题

对于没有 复杂类,主要负责处理文件系统 (数据库等)?

有点不完整。

对于我们的讨论,“单元”和“集成”之间的区别中最有用的部分是:单元测试更容易编写、维护和运行成本更低。

要编写单元测试,您只需要知道代码。如果单元测试失败,您知道这是因为代码更改。编写集成测试需要设置依赖项,例如创建具有特定内容的文件、将行插入数据库等。如果集成测试失败,可能是您的代码,也可能是您的依赖项。由于这些原因和其他原因,集成测试的创建、维护和运行更加复杂,因此成本更高。

增加的费用应该促使开发人员将封装业务逻辑的类与处理与外部系统交互的类分开,以尽量减少所需的集成测试数量。业务逻辑可以通过单元测试进行测试,成本更低。

编辑

您的类可能具有复杂的逻辑,而其本身也很复杂,因为它必须处理底层外部依赖项(即相关文件系统)中的复杂行为。在这种情况下,模拟文件系统本身可能非常困难,并且可能仅使用正确设置的文件系统并编写“集成”测试更便宜/更容易。

要记住的重要一点是您要努力实现的目标:以可接受的成本建立信心。如果“集成”测试足够便宜,那就太好了。如果您可以使用“单元”测试更便宜地获得相同的信心,那就更好了。确切的组合取决于手头的问题。

【讨论】:

  • 谢谢你,@sharakan。我听到了,因此我在问题中添加了说明,即该类没有外部依赖项。集成测试目前被称为“集成”只是因为它们使用真实的文件系统。与提议的单元测试的不同之处在于,它只是通过一些文件系统对象来做所有事情,这将在单元测试期间被模拟。
  • 类没有外部依赖,但是集成测试用的是真实的文件系统?根据大多数定义,“真正的文件系统”外部依赖项。
  • 根据您的编辑,我认为您所说的是被测试的类相当复杂,因为底层文件系统的行为很复杂,必须处理。这可能会改变成本/收益分析,我会更新我的答案。
猜你喜欢
  • 1970-01-01
  • 2015-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-07-13
  • 1970-01-01
  • 1970-01-01
  • 2017-04-08
相关资源
最近更新 更多