代码之家  ›  专栏  ›  技术社区  ›  ire_and_curses

是否存在单元测试对代码有害的情况?

  •  13
  • ire_and_curses  · 技术社区  · 15 年前

    这个站点上的大多数讨论对于单元测试都是非常积极的。我很喜欢单元测试。然而,我发现广泛的单元测试带来了它自己的挑战。例如,单元测试通常与它们测试的代码紧密耦合,这会使API更改随着测试量的增加而变得越来越昂贵。

    您是否发现了单元测试对代码质量或交付时间有害的真实情况?你是如何处理这些情况的?是否有任何“最佳实践”可应用于单元测试的设计和实现?

    这里有一个相关的问题: Why didn't unit testing work out for your project?

    9 回复  |  直到 15 年前
        1
  •  4
  •   JaredPar    15 年前

    通过大量的单元测试,您将开始发现重构操作更昂贵,这正是您所说的原因。

    我知道这是件好事。与小而便宜的变更相比,对API进行昂贵而大的变更应该有更大的成本。重构并不是一个自由的操作,理解它对您自己和您的API使用者的影响是很重要的。单元测试是衡量一个API变更将花费多大代价的重要标尺。

    不过,这个问题的一部分可以通过工具来解决。大多数IDE直接或间接(通过插件)支持其代码库中的重构操作。使用这些操作来更改单元测试将减轻一些痛苦。

        2
  •  3
  •   Wim Coenen    15 年前

    有没有什么“最佳实践”是 可应用于设计和 单元测试的实施?

    确保单元测试 尚未成为集成测试 . 例如,如果您有一个类foo的单元测试,那么理想情况下,只有在

    1. foo发生了变化
    2. 或者foo使用的接口发生了变化
    3. 或者域模型发生了变化(通常您会有一些类,如“ Customer “它们是问题空间的中心,没有抽象空间,因此不会隐藏在接口后面。”

    如果您的测试由于任何其他更改而失败,那么它们已经成为集成测试,并且随着系统规模的扩大,您将遇到麻烦。单元测试不应该有这样的可伸缩性问题,因为它们测试一个独立的 单元 代码的。

        3
  •  2
  •   ire_and_curses    15 年前

    我工作的一个项目经过了大量的单元测试;我们为20个左右的类进行了1000多个单元测试。测试代码略多于生产代码。单元测试捕获了重构操作中引入的无数错误;它们确实使更改、扩展功能等变得容易和安全。发布的代码的错误率非常低。

    为了鼓励我们自己编写单元测试,我们特别选择了保持它们“快速和肮脏”——我们在生成项目代码时会猛击一个测试,而测试是无聊的,而不是真正的代码,所以只要我们编写了一个执行生产代码功能的测试,我们就完成了,并继续进行。测试代码的唯一标准是它完全执行了生产代码的API。

    我们学到的最困难的方法是,这种方法无法扩展。随着代码的发展,我们看到需要改变对象之间的通信模式,突然间我有600个失败的单元测试!修这个花了我几天时间。这种级别的测试破坏在进一步的主要架构重构中发生了两到三次。在每一种情况下,我不相信我们能够合理地预见到预先需要的代码进化。

    这个故事的寓意是: 单元测试代码需要和生产代码一样干净 . 在单元测试中,你根本无法摆脱剪切和粘贴。您需要应用合理的重构,并尽可能使用代理对象将测试与生产代码分离。

    当然,所有这些都会给您的单元测试增加一些复杂性和成本(并且可能会给您的测试引入bug!)所以这是一个很好的平衡。但我相信,孤立地采取的“单元测试”的概念,并不是人们通常认为的清晰明确的胜利。我的经验是,单元测试和编程中的其他所有东西一样,需要小心,而不是一种可以盲目应用的方法。因此,我感到惊讶的是,我没有在像这样的论坛和文献中看到更多关于这个主题的讨论。

        4
  •  1
  •   Otávio Décio    15 年前

    大多数情况下,系统开发时没有考虑单元测试,这是一个事后考虑,而不是一个设计工具。当您使用自动化测试进行开发时,破坏API的机会会减少。

        5
  •  1
  •   Steven Sudit    15 年前

    过多的误报会减慢开发速度,因此测试您真正想要保持不变的东西是很重要的。这通常意味着提前为需求编写单元测试,然后跟踪更详细的单元测试,以检测输出中的意外变化。

        6
  •  1
  •   John Deters    15 年前

    我认为你是在寻找一个症状,而不是认识到整个问题。根本问题是,真正的API是一个已发布的接口*,它应该受到与任何编程契约相同的限制:没有更改!您可以添加到API中,并将其称为API v2,但不能返回并更改API v1.0,否则您确实破坏了向后兼容性,这对于API来说几乎总是一件坏事。

    (*我不是要调用任何特定的接口技术或语言,接口可以是从类声明开始的任何内容。)

    我建议一个测试驱动的开发方法首先可以帮助防止许多此类问题。有了TDD,您在编写测试时会“感觉”到接口的笨拙,并且您将被迫在流程的早期修复这些接口,而不是等到编写了1000个测试之后再进行修复。

    测试驱动开发的一个主要好处是,它可以为您提供关于类/接口的编程使用的即时反馈。编写测试的行为是对设计的测试,而运行测试的行为是对行为的测试。如果为某个特定方法编写测试很难或很难,那么该方法很可能被错误地使用,这意味着它是一个弱设计,应该快速重构。

        7
  •  0
  •   gradbot    15 年前

    是的,在某些情况下,单元测试可能会损害代码质量和交付时间。如果您创建了太多的单元测试,您的代码将被接口破坏,整个代码质量将受到影响。抽象是伟大的,但你可以拥有太多。

    如果您为原型或系统编写单元测试,而该原型或系统很可能发生重大更改,那么单元测试将对交付时间产生影响。在这些情况下,最好编写一个验收测试,使测试更接近端到端。

        8
  •  0
  •   Samuel Carrijo    15 年前

    如果你确信你的代码不会被重用,也不需要维护,你的项目很简单而且很短,那么你就不需要单元测试。

    单元测试对于促进更改和维护很有用。他们确实增加了一点交货时间,但它是在中长期支付。如果没有中长期,可能不需要手动测试。

    但所有这些都不太可能。所以它们仍然是一种趋势:)

    此外,有时可能是一个必要的业务决策,以减少测试时间,以便更快地实现紧急交付(稍后需要支付利息)。

        9
  •  0
  •   Kevin Pang    15 年前

    缓慢的单元测试通常对开发有害。当单元测试成为需要命中Web服务或数据库的集成测试时,通常会发生这种情况。如果你的一套单元测试需要一个小时的时间运行,你经常会发现你和你的团队在那一小时内基本上瘫痪了,等着看单元测试是否通过(因为你不想在一个破碎的基础上继续建造)。

    这么说,我觉得好处是 远的 除了最做作的案件以外,其他所有案件的缺点都比不上。