我们的团队正在努力开发TDD,并努力进行单元测试的最佳实践。我们的测试代码使用依赖注入(inject)。我们的测试通常遵循Arrange-Act-Assert类型的布局,在该布局中,我们使用Moq模拟Arrange部分中的依赖项。
从理论上讲,单元测试应该是在重构时保护您的屏障。但是它正在变成阻碍我们这样做的 anchor 。我正在尝试确定过程失败的原因。
考虑简化的示例:
我希望不需要重构 Controller 测试,而是向我证明我的新 Controller 实现遵循未更改的契约(Contract)。但是我们在这里失败了,因为事实并非如此。
每个 Controller 测试都会动态模拟存储库接口(interface)。它们都需要更改。此外,由于每个测试都不想模拟所有接口(interface)和方法,因此我们发现测试与特定的实现相关,因为它需要知道要模拟的方法。
对于我们拥有的更多测试,重构变得越来越困难!或更准确地说,我们模拟接口(interface)的次数越多。
所以我的问题是:
谢谢!
最佳答案
您不会错过任何原则,但这是一个普遍的问题。我认为每个团队都以自己的方式解决(或不解决)。
副作用
任何具有副作用的功能都将继续出现此问题。我发现有副作用功能,我必须进行测试以确保某些或所有以下各项:
在测试中确保这一点通常意味着违反封装(我与实现进行交互并知道实现)。每当您执行此操作时,您总是会隐式地将测试与实现耦合。每当您更新要公开/测试的实现部分时,这将导致您必须更新测试。
可重复使用的 mock
我已经使用了可重复使用的模拟来取得巨大的效果。折衷是它们的实现更加复杂,因为它需要更完整。您确实减少了更新测试以适应重构的成本。
接受TDD
另一个选择是更改要测试的内容。由于这确实是在改变您的测试策略,因此请不要轻描淡写。您可能需要先做一点分析,看看它是否真的适合您的情况。
我曾经用单元测试来做TDD。我遇到了一个我认为不应该解决的问题。特别是在重构方面,我注意到我们通常必须更新许多测试。这些重构不在代码单元之内,而是对主要组件的重构。我知道很多人会说问题是频繁的大更改,而不是单元测试。大的变化可能是有些道理,这部分是我们的计划/架构造成的。但是,导致方向发生变化的业务决策也是如此。这些和其他合理的原因导致必须对代码进行大量更改。最终结果是,由于所有测试更新,大型重构变得更加缓慢和痛苦。
由于单元测试未涵盖的集成问题,我们还遇到了错误。我们通过人工验收测试做了一些。实际上,我们做了很多工作来使验收测试尽可能地降低接触性。它们仍然是手动的,我们觉得在单元测试和验收测试之间有太多的交叉,应该有一种方法来减轻两者的成本。
然后公司裁员。突然之间,我们没有足够的资源来进行编程和维护。我们被迫在包括测试在内的所有工作中获得最大的返回。我们从添加所谓的部分堆栈测试开始,以解决我们遇到的常见集成问题。事实证明它们是如此有效,以至于我们开始进行较少的经典单元测试。我们还摆脱了手工验收测试( Selenium )。我们慢慢提高了测试开始进行测试的位置,直到我们基本上在进行验收测试,但没有使用浏览器。我们将针对特定 Controller 模拟GET,POST或PUT方法,并检查接受标准。
我们结束了更少的错误。具体而言,几乎所有集成错误以及由于大型重构而导致的错误几乎完全消失了。
权衡取舍。事实证明,有利条件远远超过了不利条件。缺点:
关于unit-testing - 可重用的模拟与每个测试中的模拟,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4628294/