可重复使用的模拟与每次测试中的模拟

Cra*_*ste 10 unit-testing moq mocking

我们的团队正在进入TDD并努力进行单元测试的最佳实践.我们的测试代码使用依赖注入.我们的测试通常遵循Arrange-Act-Assert类型的布局,其中我们使用Moq在Arrange部分中模拟依赖关系.

从理论上讲,单元测试应该是一个屏蔽,可以在您重构时保护您.但它正在变成一个阻止我们这样做的锚.我正在努力确定我们的流程失败的地方.

考虑简化的例子:

  • XRepository.Save更改了签名和行为/合同.
  • XController.Save使用XRepository.Save,因此它被重构为使用新接口.但在外部,它的公共合同没有改变.

我预计控制器测试并没有需要进行重构,而是证明给我看我的新控制器实现荣誉的不变的合同.但我们在这里失败了,因为事实并非如此.

每个控制器测试都会动态地模拟存储库接口.他们都需要改变.此外,由于每个测试都不想模拟所有接口和方法,我们发现我们的测试与特定实现相关联,因为它需要知道要模拟的方法.

对于我们拥有的更多测试,重构变得越来越难以实现!或者更准确地说,我们模拟界面的次数越多.

所以我的问题:

  1. 是否有偏好在每个测试中使用动态模拟,而不是为每个界面制作可重复使用的手工模拟?

  2. 鉴于我的故事,我错过了一些原则还是陷入了常见的陷阱?

谢谢!

die*_*dha 10

你不缺少任何原则,但这是一个常见的问题.我认为每个团队都以自己的方式解决(或不解决).

副作用

对于任何有副作用的函数,您将继续遇到此问题.我找到了副作用函数,我必须进行测试,以确保以下部分或全部:

  • 那是/没有被召唤
  • 它被调用的次数
  • 传递给它的论点是什么
  • 通话顺序

在测试中确保这一点通常意味着违反封装(我与实现交互并知道).无论何时执行此操作,您都将隐式地将测试与实现相结合.这将导致您必须在更新正在公开/测试的实现部分时更新测试.

可重复使用的模拟器

我使用了可重复使用的模拟效果很好.权衡取舍是它们的实施更复杂,因为它需要更加完整.您确实可以降低更新测试的成本以适应重构.

验收TDD

另一种选择是改变您正在测试的内容.由于这实际上是关于改变您的测试策略,因此不能轻易进入.您可能希望先进行一些分析,看看它是否真的适合您的情况.

我以前用单元测试做TDD.我遇到了一些我认为不应该处理的问题.特别是在重构周围,我注意到我们通常不得不更新许多测试.这些重构不是代码单元,而是主要组件的重组.我知道很多人会说问题是频繁的大变化,而不是单元测试.对于我们的规划/架构部分导致的大变化,可能有一些道理.但是,业务决策也导致了方向的变化.这些和其他合法原因导致需要对代码进行大量更改.最终的结果是,由于所有测试更新,大型重构变得更加缓慢和痛苦.

由于单元测试未涵盖的集成问题,我们也遇到了错误.我们通过手动验收测试做了一些.我们实际上做了很多工作,使验收测试尽可能低.它们仍然是手动的,我们觉得在单元测试和验收测试之间存在很多交叉,应该有一种方法可以降低实现两者的成本.

然后该公司裁员.突然间,我们没有相同的资源来投入编程和维护.我们被推动为我们所做的一切获得最大的回报,包括测试.我们首先添加了所谓的部分堆栈测试,以涵盖我们遇到的常见集成问题.事实证明它们非常有效,我们开始进行不那么经典的单元测试.我们还摆脱了手动验收测试(Selenium).我们慢慢推高测试开始测试的地方,直到我们基本上进行验收测试,但没有浏览器.我们将模拟GET,POST或PUT方法到特定的控制器并检查验收标准.

  • 数据库已正确更新
  • 返回了正确的HTTP状态代码
  • 返回的页面是:
    • 是有效的HTML 4.01严格
    • 包含我们想要发回给用户的信息

我们结束了更少的错误.特别是几乎所有的集成错误,以及由于大型重构导致的错误几乎完全消失.

有权衡.事实证明,专业人士远远超过了出局的缺点.缺点:

  • 测试通常更复杂,几乎每个人都测试一些副作用.
  • 我们可以判断什么时候出现问题,但它不像单元测试那样有针对性,所以我们必须做更多的调试来追踪问题所在.