在过去的几年中,我们一直在对现有产品进行单元测试,以提高其内部质量。 在此期间,我们始终面临选择单元对集成测试的挑战。 我想提一下我们为改善现有系统质量而采用的一些方法。
单元测试的核心是通过隔离依赖项来一次测试单个组件。 经典的单元测试具有“快速,独立,可重复,自验证,及时”的属性。 通常,在Java中,方法被视为一个单元。 因此,传统的(也是最常见的)方法是测试与所有依赖项分离的类的单个方法。
有趣的是,没有“由什么构成单位”的硬性定义。 很多时候,跨越多个类的方法的组合可以形成一个单一的行为。 因此,在这种情况下,应将行为视为一个单元。 我见过有人打破这些单元并编写多个测试以测试一种方法。 如果中间结果不重要,则只会增加系统的复杂性。 测试系统的最佳方法是在我们可以容纳它们的地方测试其依赖项。 因此,我们应该尝试使用实际的实现而不是模拟。 鲍伯叔叔很好地指出了这一点:“模拟具有建筑意义的边界,但不要跨越那些边界。” 在他的文章中 。
如果软件是使用TDD方法构建的,那么隔离依赖关系或为下一个功能添加测试可能不是挑战。 但是,并非所有软件都是这样构建的。 不幸的是,我们的系统只编写了很少的测试或没有编写任何测试。 在使用这些系统时,我们可以利用上述原理并在不同级别上使用测试。 尹泰瑞(Terry Yin)在题为“单元测试的误解”的演讲中提供了出色的图形(如下所示)。 这显示了不同的测试如何可以增加值,以及它的缺点是什么。
我们的许多项目都使用Java和Spring框架。 我们已经使用springs @ RunWith和SpringJUnit4ClassRunner来创建AppLevel Tests,它为您提供了所有依赖项已启动的对象。 如果您想隔离某些依赖项,则可以有选择地模拟它们。 这为编写带有多个协作对象的单元测试提供了一个很好的平台。 我们称它们为应用级测试。 这些仍然是快速运行的测试,没有任何外部依赖项。 选择了不同的术语来区别于经典的单元测试。 我们还进行了集成测试,该测试可以与外部系统连接。 因此,开发人员测试的总体情况可以总结如下:
| 测验 | 命名约定 | 运行于 | 何时使用 | 执行时间 |
|---|---|---|---|---|
| 单元测试 | 以测试结束 | 每一个版本 | 基于规则的实现,可以单独测试逻辑 | 几毫秒 |
| 应用级别测试 | 以TestApp结尾 | 每次构建/每晚构建(团队选择) | 测试与其他服务层相关的服务层。 使您摆脱创建模拟对象的麻烦。 应用程序上下文已加载到测试中。 | 几秒钟 |
| 整合测试 | 以TestIntg结尾 | 在构建中使用特殊配置文件时,按需运行。 | 以上所有功能+当您需要连接到外部点(例如DB,Web服务等)时使用。 | 取决于集成点。 |
| 手动运行测试 | 以TestIntgManual结尾 | 手动运行测试,用于在本地调试特定问题 | 以上所有-无法实现自动化。 | 取决于集成点。 |
这种方法使开发人员可以选择正确的抽象级别进行测试,并有助于优化他们的时间。 如今,我的默认选择是应用程序级别测试,如果我要实现复杂的逻辑,我将进行单元测试。
进一步阅读:
- http://martinfowler.com/bliki/UnitTest.html
- http://blog.manupk.com/2011/11/when-to-replace-unit-tests-with.html
- http://blog.8thlight.com/uncle-bob/2013/03/05/TheStartUpTrap.html
- http://codeofrob.com/entries/uncle-bobs-viewpoint-considered-harmful.html
- http://blog.8thlight.com/uncle-bob/2014/05/10/WhenToMock.html
- http://bryanpendleton.blogspot.in/2015/04/on-testing-strategies-and-end-to-end.html
- https://www.symphonious.net/2015/04/30/making-end-to-end-tests-work/
- https://www.thoughtworks.com/insights/blog/mockists-are-dead-long-live-classicists
翻译自: https://www.javacodegeeks.com/2016/08/approach-help-developers-write-meaningful-tests.html