我已经阅读(并重新阅读)Martin Fowler 的Mocks Aren't Stubs。在其中,他定义了两种不同的 TDD 方法:“Classical”和“Mockist”。他试图回答“那么我应该成为古典主义者还是模仿者? ”这个问题,但他承认他从未在“除了玩具之外的任何东西”上尝试过模仿者 TDD。所以我想我会在这里问这个问题。好的答案可能会重复 Fowler 的论点(但希望更清楚)或添加自 Fowler 上次于 2007 年 1 月更新文章以来他没有想到的或其他人提出的论点。
5 回答
我认为您不需要选择其中之一。两者都有其优点和缺点,两者都是您工具箱的工具。“Mockist” tdd 使您在可以测试的内容上更加灵活,而经典 TDD 使您的测试不那么脆弱,因为它们倾向于更多地查看输入/输出而不是查看实际的实现。在进行模拟单元测试时,我似乎在更改实现时有更多测试中断。
我尽可能使用经典的 tdd(尽管我经常使用模拟框架来快速设置存根)。有时我注意到我一次开始测试太多,或者我需要太多对象来设置测试。那时,mockist 测试通常可以帮助您设置较小的测试。
这一切都很抽象,所以我希望我有意义
关于 mockist 或经典 tdd 的问题很大程度上是关于您正在测试应用程序的哪一部分。如果您有一个“标准”分层架构(例如DDD),那么域层通常适合经典的 tdd,您可以在其中通过设置被测对象进行单元测试,调用一些方法并检查结果和/或状态。
另一方面,当您测试应用程序服务、控制器或表示逻辑时,它们都进行更多的协调工作,通常需要模拟或存根来获得良好的测试。我的经验也是,这些类往往会调用您真正想要模拟或存根的其他层(webservice、datalayer...)。这些单元测试还需要更多设置代码,因此您应该只在必要时进行模拟。
我的建议是尽可能经典,必要时模仿。
您可以考虑查看我们的书,网址为http://www.growth-object-orientated-software.com/。它包括一个扩展的工作示例。当我们写它时,我们发现状态与交互的区别在很大程度上是误导性的,它更多的是关于一个人的面向对象设计的方法。
Sandi Metz 揭露了一种非常务实的做法:
一个对象可以通过传出或传入消息与其他对象通信。消息可以是查询(返回某些内容)或命令(执行某些内容)。
有四种组合。不应测试传出查询消息(已作为外部类的传入查询进行测试)您可以对传出命令消息使用 mockist 测试方法,并对其余消息使用经典测试。
检查链接
http://jnoconor.github.io/blog/2013/10/07/the-magic-tricks-of-testing-by-sandi-metz/
https://speakerdeck.com/skmetz/magic-tricks-of-testing-ancientcityruby
我在 TDD 方面还是比较新的——但我被教导/介绍差异的方式是从测试类之间的集成的角度来考虑它,这样你就不会依赖实时数据。例如,如果我有一个非常独立的类 - 不依赖于我为项目构建的其他类,并且它不会进入实时数据/开发环境进行输入(如 DB 或 API一个系统)然后我只会在 NUnit 或 JUnit 之类的东西中使用经典单元测试 - 但是当我开始测试构建类之间的交互时 - 那时它可以真正方便地模拟其他自定义类和/或外部交互 - 这样你可以挑出并测试您当前类的代码,而无需试图追查您正在调用的其他类中的潜在错误。