原创 | 使用 JUnit、AssertJ 和 Mockito 编写单元测试和实践 TDD (三)单元测试在整个测试体系中的位置
上一章讲到“为什么要写单元测试?”,这一章我们讲讲“单元测试在整个测试体系中的位置”
单元测试是软件测试体系中的一员。
1. 软件测试的类别
软件测试主要有以下这些类别:
单元测试。对底层代码的每个工作单元进行测试。通常涉及一个类的一个公开方法。白盒测试。细粒度。
集成测试。中等粒度的测试。对多个类之间的协作或与外部系统的交互以完成某项内部功能进行测试。涉及多个类的交互。白盒测试。中等粒度。
功能测试。粗粒度的测试。针对系统的每个用例(对客户或用户有用的功能)进行测试。黑盒测试。粗粒度。
性能和负载测试。测试系统的性能和吞吐量等。黑盒测试。
验收测试。从用户界面开始对整个系统进行测试验收,检验系统是否已经准备就绪,可以部署生产。
说明:
白盒和黑盒测试。白盒测试是看得见内部代码情况下进行的测试,包括单元测试和集成测试。由程序员而不是测试人员编写和运行,用于验证自己写的代码和/或交互是否正确。黑盒测试是不了解/不关心系统的代码和内部结构的情况下进行的测试,包括功能测试、性能和负载测试以及验收测试。由测试人员编写和运行,用于验证系统的功能和表现是否满足客户和用户的需要。
外部功能与内部功能。外部功能代表一项功能需求(例如存款、取款、申报、索赔等等),由客户或产品经理定义,是系统的业务价值所在,呈现系统的外部可见的属性和行为。内部功能由开发人员定义,可能是业务性的,也可能是技术性的,用于实现外部功能和质量,呈现系统内部的结构和机制。通常一项外部功能由多个内部功能协作完成。
协作者和外部系统。要完成一项功能,通常都涉及到多个类之间,或者类与外部系统之间的交互协作。与其他类之间交互的例子是:下订单的时候,订单服务类要调用定价服务类来获取订单中每项商品的当前价格。与外部系统的交互包括与数据库、缓存、消息中间件、文件系统、第三方服务提供者等等的交互。在单元测试中,要对所有的协作者和外部系统进行隔离和模拟。其他测试通常直接使用真实的协作者和外部系统,而不进行模拟。集成测试有时候会对部分外部系统进行模拟。
2. 测试金字塔
Mike Cohn 在他的著作《Succeeding with Agile》一书中提出了这个测试金字塔这个概念。这个比喻非常形象,它让你一眼就知道测试是需要分层的。它还告诉你每一层需要写多少测试。
在上图中,金字塔从下往上分别是单元测试、服务测试(相当于我们上面分类中的功能测试)和用户界面测试(相当于我们上面分类中的验收测试)。测试金字塔说明:
越是底层的测试,测试的数量越多;越是上层的测试,测试的数量越少。单元测试的测试量是最大的。每个工作单元都需要编写测试。实际上每个工作单元通常都需要写多个测试,以保证对各种可能条件进行全面测试覆盖。
越是底层的测试,越需要能够快速执行。上层的测试则允许更长时间完成。所有的单元测试都应该能够在毫秒级时间内完成。
越是底层的测试,越需要隔离依赖项。越高层的测试,越需要集成更多的真实依赖项。底层的单元测试,要对一切依赖项(系统中的其他类和外部系统)进行隔离和模拟;而上层的测试需要集成系统中其他的类、子系统和外部系统,进行联合测试。
3. 各种测试不可相互取代
各种测试之间不可相互代替。例如,单元测试用于保证最底层的每个工作单元的正确性(零件的正确性),集成测试用于保证多个类协作完成高阶目标(内部功能)的正确性(组装的正确性)。作为技术人员,我们肯定要首先保证每个零件没问题,然后也要保证这些零件组装起来后能够完成某项期待的功能。
单元测试作为最基础的测试,其重要性不可言喻。下层基础不牢靠,上层建筑就会风雨飘摇。如果只有集成测试,没有单元测试。那么当集成测试失败之后,你就无法确定是零件导致的失败,还是组装导致的失败。定位和修复bug就需要消耗大量的成本。如果已经有单元测试保证零件的正确性,当集成测试失败时,我们就可以肯定是组装的问题,而不是零件的问题,从而可以以较低的成本定位和修复bug。
下一章将讲讲“单元测试的常见错误观念和做法”!
版权声明: 本文为 InfoQ 作者【编程道与术】的原创文章。
原文链接:【http://xie.infoq.cn/article/2b1cebae4bb9fe3911f052790】。文章转载请联系作者。
评论