【问题标题】:Does stub make tests fragile?存根会使测试变得脆弱吗?
【发布时间】:2022-10-22 23:04:40
【问题描述】:

假设我们的 SUT(A 类)具有依赖项(IDependency),并且我们为某些操作(返回 bool 值的方法 A)创建带有固定答案的存根。

通过这种方式,我们揭示了 SUT 的一些实现细节(使用方法 A 的 SUT),如果我们想在不破坏原始行为的情况下重构 SUT(而不是使用方法 B 的方法 A,该方法也返回 bool 值)。

根据 Vladimir Khorikov 的书(单元测试),我们的测试对重构没有抵抗力。

问题是: 存根会使测试变得脆弱吗?

【问题讨论】:

  • 每当您模拟/存根/伪造依赖项的逻辑时,您就有可能无法真正匹配它的真实行为。事情就是这样运作的。
  • 风险在于,每次我重构代码时,我的测试都会失败,因为我在夹具设置中设置了方法 A 而不是方法 B。我的测试失败但 SUT 工作正常(误报)

标签: unit-testing testing automated-tests stub test-double


【解决方案1】:

正如 luk2302 提到的。测试替身(模拟、存根、伪造等)增加了测试和被测系统之间的耦合。单元测试的目的应该是测试行为,而不是实现(这是 Kent Beck 提出 TDD 时的目标)。

但有时从长远来看,替换依赖项更容易、更便宜。当依赖项是组件的一个边界时,这一点很明显,例如一个会替换一个调用外部系统的类。另一方面,在对与我正在测试的类位于同一包中的类使用测试替身之前,我会三思而后行;如果它们在同一个包中,则意味着它们密切相关,可以一起测试它们。拥有一个不与外部世界交互的纯领域模型有很大帮助。

另外,如果一个测试变得更复杂并且不容易理解,这表明它可能覆盖太多,我需要开始拆分;添加测试替身,并可能将生产代码移动到其他类/包。

我想强调一个 SUT 不需要是一个单一的类。现在很多人都犯了这个错误,认为只需要测试一个类并模拟其他所有内容。即使是提出了 London/Mockist 测试学派的人也从未提出过这种嘲笑一切的方法。

【讨论】:

  • 我在单元测试中使用状态验证,根据 Gerard Meszaros 的书(xUnit 测试模式),我们应该只关心 SUT 的最终状态,而不是 SUT 是如何到达那里的。如果 SUT 将来重构并使用方法 B 而不是方法 A,那么我的测试实际上是什么?它使用依赖方法 A 测试 A 类,而 Sut 实际上使用方法 B 并且没有测试失败。我该如何解决这个问题?
  • 这是一个平衡。我们仍然需要测试替身。重要的方面是在需要时使用它们,而不是像您所说的那样测试 SUT 是如何到达那里的。没有正确的答案,通过经验你会感觉到何时使用测试替身或使用真正的实现。提示:任何维持疼痛的测试都很好地表明当前方向不好。在这方面有一个低的疼痛阈值是件好事,而不是成为英雄:)。你读过那些书,所以我会说你走在正确的轨道上。
猜你喜欢
  • 1970-01-01
  • 2010-10-02
  • 2014-10-21
  • 1970-01-01
  • 1970-01-01
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多