tdd-单元测试的采用

我们试图将单元测试引入我们当前的项目中,但是似乎没有用。 额外的代码似乎已成为维护工作的头疼问题,因为当内部框架发生更改时,我们必须四处寻找并修复所有与之相关的单元测试。

我们有一个用于对控制器进行单元测试的抽象基类,它充当调用子类的抽象方法实现的模板,即框架调用Initialize,因此我们的控制器类都有自己的Initialize方法。

我曾经是单元测试的提倡者,但是它似乎不适用于我们当前的项目。

谁能帮助您确定问题以及我们如何使单元测试对我们有用而不是不利于我们?

Burt asked 2019-09-27T05:06:55Z
19个解决方案
107 votes

提示:

避免编写程序代码

如果测试是针对严重依赖全局状态或深陷于丑陋方法中的程序式代码编写的,则可能会让人难以忍受。如果您使用OO语言编写代码,请有效使用OO构造来减少这种情况。

  • 尽可能避免使用全局状态。
  • 避免使用静态变量,因为它们往往会在您的代码库中波动,并最终使某些事情本来应该是静态的。 它们还会夸大您的测试环境(请参见下文)。
  • 有效利用多态性来防止过多的if和flags

找到什么变化,将其封装并与保持不变的部分分开。

代码中的阻塞点比其他部分更频繁地更改。 在您的代码库中执行此操作,您的测试将变得更加健康。

  • 良好的封装导致良好的,松散耦合的设计。
  • 重构和模块化。
  • 保持测试规模小,重点突出。

围绕测试的上下文越大,维护起来就越困难。

竭尽所能缩小测试范围以及执行测试的周围环境。

  • 使用组合方法重构来测试较小的代码块。
  • 您是否正在使用诸如TestNG或JUnit4之类的更新测试框架?通过为您提供测试生命周期中的更多细粒度的钩子,它们使您可以消除测试中的重复项。
  • 使用测试双打(模拟,假冒,存根)进行调查以减小测试上下文的大小。
  • 研究“测试数据构建器”模式。

从测试中删除重复项,但确保它们保持焦点。

您可能无法删除所有重复项,但仍尝试在引起痛苦的地方将其删除。 确保您没有删除太多重复项,以至于某人无法进入并一目了然地告诉您测试的内容。 (有关同一概念的另一种解释,请参阅Paul Wheaton的“邪恶的单元测试”文章。)

  • 如果他们不知道自己在做什么,谁也不想修复测试。
  • 遵循编排,执行,断言模式。
  • 每个测试仅使用一个断言。

在正确的级别上测试要验证的内容。

考虑一下Selenium记录和回放测试所涉及的复杂性,以及与测试单一方法相比,您可能会发生的变化。

  • 相互隔离依赖关系。
  • 使用依赖项注入/控件反转。
  • 使用测试双精度来初始化要测试的对象,并确保要独立测试单个代码单元。
  • 确保您正在编写相关的测试
    • 通过有意引入一个错误并确保它被测试捕获来“弹跳陷阱”。
  • 另请参阅:集成测试是骗局

知道何时使用基于状态的测试与基于交互的测试

真正的单元测试需要真正的隔离。 单元测试不会打数据库或打开套接字。 不要嘲笑这些互动。 验证您是否与协作者正确交谈,而不是此方法调用的正确结果是“ 42”。

演示测试驱动代码

一个特定的团队是否将测试所有代码进行测试,还是为每行代码编写“测试优先”,尚有待商debate。 但是他们应该至少首先编写一些测试吗? 绝对。 在某些情况下,首先测试无疑是解决问题的最佳方法。

  • 尝试此练习:就像您是故意的那样进行TDD(另一个说明)
  • 另请参阅:测试驱动开发和科学方法

资源:

  • 由Lasse Koskela驾驶测试
  • 不断发展的OO软件,以Steve Freeman和Nat Pryce的测试为指导
  • 通过迈克尔·费瑟斯有效地使用遗留代码
  • Gojko Adzic举例说明
  • 签出博客:Jay Fields,Andy Glover,Nat Pryce
  • 如其他答案中所述:
    • XUnit模式
    • 测试气味
    • Google测试博客
    • Miskov Hevery的“面向可测试性的OO设计”
  • 保罗·惠顿(Paul Wheaton)的“邪恶单元测试”
  • J.B. Rainsberger撰写的“集成测试是骗局”
  • J.B. Rainsberger撰写的“软件设计经济学”
  • Rick Mugridge的“测试驱动的开发和科学方法”
  • 最初由Keith Braithwaite主持的“ TDD,好像你是真的意思”演习,也由Gojko Adzic主持
cwash answered 2019-09-27T05:12:58Z
19 votes

您是否正在测试足够小的代码单元? 除非您从根本上更改核心代码中的所有内容,否则您应该不会看到太多更改。

一旦一切稳定,您将更喜欢单元测试,但是即使现在您的测试也突出了对框架更改进行传播的程度。

这是值得的,尽您所能坚持下去。

cjk answered 2019-09-27T05:13:36Z
12 votes

没有更多信息,很难弄清楚为什么您遭受这些问题。 有时不可避免的是,更改接口等会破坏很多事情,而其他时候则是设计问题。

尝试对遇到的故障进行分类是个好主意。 你有什么问题? 例如。 是由于API更改而测试维护(如使其在重构后进行编译!),还是归因于API更改的行为? 如果可以看到模式,则可以尝试更改生产代码的设计,或者更好地隔离测试以免更改。

如果更改一些事情在很多地方对您的测试套件造成了难以估量的破坏,那么您可以做一些事情(其中大多数只是常见的单元测试技巧):

  • 开发少量的代码并进行测试一小段代码。 提取接口或基类在哪里有道理,以便代码单位有“接缝”。 越多您必须引入的依赖项(或更糟糕的是,在班级内部实例化使用“新”),更改您的代码即可。 如果每个代码单元有几个依赖性(有时是一对或一点都没有)那更好不受变化的影响。

  • 只断定要测试什么需要。 不要在中间断言,偶然或无关的状态。 由...设计合同和按合同进行测试(例如如果您正在测试堆栈弹出方法,之后不要测试count属性推动-应该在单独测试)。

    我看到这个问题相当多,特别是如果每个测试是一个变体。 如果有的话偶然的状态变化会破坏主张的一切(是否需要断言或不)。

  • 与普通代码一样,使用工厂和构建器在单元测试中。 大约40次测试时我才知道需要在API更改后更新构造函数调用...

  • 同样重要的是,使用前门第一。 您的测试应始终使用正常状态(如果可用)。 仅在必要时(即没有要验证的状态)使用基于交互的测试。

无论如何,要点是我将尝试找出测试中断的原因/位置并从那里去。 尽最大努力使自己与变化隔离。

Mark Simpson answered 2019-09-27T05:14:57Z
8 votes

单元测试的好处之一是,当您进行这样的更改时,可以证明您没有破坏代码。 您的确必须使测试与框架保持同步,但是,这项相当繁琐的工作比尝试找出重构时发生的故障要容易得多。

Jon B answered 2019-09-27T05:15:22Z
4 votes

我会坚持要求您坚持TDD。 尝试检查您的单元测试框架是否与您的团队进行一次RCA(根本原因分析),并确定区域。

将单元测试代码固定在套件级别,并且不要经常更改代码,尤其是功能名称或其他模块。

如果您能很好地分享您的案例研究,将不胜感激,那么我们可以在问题领域进行更多的挖掘吗?

Sourabh answered 2019-09-27T05:15:59Z
4 votes

好问题!

设计好的单元测试与设计软件本身一样困难。 开发人员很少意识到这一点,因此结果通常是草率编写的单元测试,每当被测系统发生变化时,都需要维护。 因此,解决您的问题的部分方法可能是花费更多的时间来改进单元测试的设计。

我可以推荐一本值得一提的出色书籍,即《单元测试的设计模式》。

HTH

azheglov answered 2019-09-27T05:16:39Z
4 votes

如果问题是您的测试与实际代码已过时,则可以执行以下一项或两项操作:

  1. 训练所有开发人员不要通过不会更新单元测试的代码审查。
  2. 设置一个自动测试框,在每次签入后运行完整的单元测试,并通过电子邮件将中断构建的人员发送给您。 (我们曾经以为那只是针对“大男孩”,但我们在专用盒子上使用了开源软件包。)
Robert Gowland answered 2019-09-27T05:17:17Z
3 votes

好吧,如果代码中的逻辑发生了变化,并且您已经为这些代码段编写了测试,那么我认为需要更改测试以检查新的逻辑。 单元测试应该是测试代码逻辑的相当简单的代码。

CSharpAtl answered 2019-09-27T05:17:43Z
3 votes

您的单元测试正在执行应做的事情。 揭露由于框架,即时代码或其他外部源的更改而导致的行为中断。 这样做的目的是帮助您确定行为是否确实发生了变化,是否需要相应地修改单元测试,或者是否引入了错误从而导致单元测试失败并需要更正。

不要放弃,尽管它现在令人沮丧,但其好处将会实现。

David Yancey answered 2019-09-27T05:18:13Z
2 votes

我不确定会使您的代码难以维护测试的特定问题,但是当我遇到类似的问题导致测试中断时,我可以分享一些自己的经验。 我最终了解到,缺乏可测试性的主要原因是被测类的一些设计问题:

  • 使用具体的类而不是接口
  • 使用单例
  • 调用大量用于业务逻辑和数据访问的静态方法,而不是接口方法

因此,我发现我的测试通常会中断-不是由于被测类的更改-而是由于被测类正在调用的其他类的更改。 通常,重构类以请求其数据依赖性并使用模拟对象进行测试(EasyMock等针对Java的测试)使测试更加集中和可维护。 我真的很喜欢一些与此主题有关的网站:

  • Google测试博客
  • 编写可测试代码的指南
Kyle Krull answered 2019-09-27T05:19:17Z
2 votes

为什么每次更改框架时都必须更改单元测试? 难道不是这样吗?

如果使用的是TDD,则应首先确定测试正在测试错误的行为,而应改为验证所需的行为是否存在。 现在,您已经修复了测试,测试将失败,并且必须消除框架中的错误,直到测试再次通过。

SingleNegationElimination answered 2019-09-27T05:19:49Z
1 votes

当然,一切都附带价格。 在开发的早期阶段,通常必须更改许多单元测试。

您可能想查看一下代码中的一些内容,以进行更多的封装,创建更少的依赖项等。

当您临近生产日期时,您会很高兴拥有这些测试,请相信我:)

Gerrie Schenck answered 2019-09-27T05:20:26Z
1 votes

您的单元测试不是面向黑盒的吗? 我的意思是...让我举个例子:假设您正在对某种容器进行单元测试,是否使用容器的get()方法来验证新项是否已实际存储,或者您是否设法获取了 实际的存储库可以直接检索存储在哪里的项目? 后者进行脆弱的测试:更改实现时,您将破坏测试。

您应该针对接口而不是内部实现进行测试。

当您更改框架时,最好先尝试更改测试,然后再更改框架。

philant answered 2019-09-27T05:21:06Z
1 votes

我建议您投资测试自动化工具。 如果您使用的是持续集成,则可以使其协同工作。 有一些工具可以扫描您的代码库并为您生成测试。 然后将运行它们。 这种方法的缺点是它太通用了。 因为在许多情况下,单元测试的目的是破坏系统。我编写了许多测试,是的,如果代码库更改,我必须更改它们。

自动化工具有一条很好的界线,您肯定会更好地覆盖代码。

但是,使用编写良好的基于开发人员的测试,您还将测试系统完整性。

希望这可以帮助。

Boris Kleynbok answered 2019-09-27T05:21:47Z
1 votes

如果您的代码确实很难测试并且测试代码中断或需要付出很大努力才能保持同步,那么您会遇到更大的问题。

考虑使用提取方法重构来抽取一小段代码,这些代码只能完成一件事,而只有一件事; 没有依赖关系,并将您的测试写到那些小的方法。

sal answered 2019-09-27T05:22:19Z
1 votes

额外的代码似乎已成为维护工作的头疼问题,因为当内部框架发生更改时,我们必须四处寻找并修复所有与之相关的单元测试。

另一种选择是,当您的框架发生更改时,您无需测试更改。 否则,您根本不会测试框架。 那是你要的吗?

您可以尝试重构您的框架,使其由可以独立测试的较小部分组成。 然后,当您的Framework发生更改时,您希望(a)更改的部分更少或(b)更改的主要部分是组成这些部分的方式。 两种方式都可以使您更好地重用代码和测试。 但是涉及真正的智力努力。 不要指望那么容易。

Norman Ramsey answered 2019-09-27T05:22:58Z
1 votes

我发现,除非您使用IoC / DI方法鼓励编写非常小的类,并且认真地遵循“单一职责原则”,否则单元测试最终会测试多个类的交互,这会使它们非常复杂,因此很脆弱。

我的观点是,许多新颖的软件开发技术只能在一起使用时才能起作用。 特别是MVC,ORM,IoC,单元测试和模拟。 DDD(在现代原始意义上)和TDD / BDD更加独立,因此您可以使用或不使用它们。

zvolkov answered 2019-09-27T05:23:29Z
0 votes

有时,设计TDD测试会引发对应用程序本身设计的质疑。 检查您的类是否设计得很好,您的方法一次只执行一件事。如果设计得好,编写代码来测试简单的方法和类应该很简单。

sptremblay answered 2019-09-27T05:23:54Z
0 votes

我自己一直在考虑这个话题。 我对单元测试的价值非常满意,但对严格的TDD却不满意。 在我看来,在某种程度上,您可能正在进行探索性编程,其中您需要将事物划分为类/接口的方式进行更改。 如果您在旧的类结构的单元测试上投入了大量时间,那将增加重构的惯性,或者很难舍弃这些额外的代码,等等。

Anon answered 2019-09-27T05:24:18Z
translate from https://stackoverflow.com:/questions/920992/unit-test-adoption