单元测试是白盒测试,又被称为模块测试,是最小的测试单元。

单测应该做到:

  • 一次检测一个方法
  • 提供方法所需的参数
  • 验证结果的准确性

Why

为什么需要单测

  • 正确性保证
  • 提高代码质量,高内聚,低耦合
  • 代码重构时回归验证

测试内容

测试的内容可以包括

  • 模块接口
  • 局部数据结构
  • 分支路径
  • 错误处理
  • 边界测试

评价测试的指标,覆盖范围,是否测试代码覆盖了基本表达语句,基本逻辑块。

  • 语句覆盖,每一个基本语句是否被覆盖
  • 判定覆盖,分支的每一个路径是否都被覆盖
  • 循环覆盖,循环体,0,1,多次是否都被覆盖

Mock 工具

Mock 使用场景

  • 依赖外部调用
  • DAO 层,数据库等底层存储调用
  • 系统间异步通知
  • 应用中类(Abstract,final,static),接口等

Mock 工具工作的过程

  • Record 过程,准备数据阶段,创建依赖的 Class,Interface 后者 Method,模拟返回的数据,耗时,调用次数等等
  • Replay 阶段,调用被测试代码,执行测试,期间 Invoke 上一阶段准备的 Mock 对象或者方法
  • Verify 阶段,验证调用是否正确,Mock 方法调用次数,顺序等等

目前比较流行的 Mock 工具有 EasyMock,jMock,Mockito,Unitils,Mock,PowerMock,JMockit 等等

Best practices

Unit test 执行前后不能对环境造成污染,避免写有副作用的 TestCase

自我清理:

  • 文件
  • H2 内存数据库
  • MySQL 事务回滚 @TransactionConfiguration(transactionManager = "transactionManager", defaultRollback = true)

保持效率

  • Unit test 中不要 Sleep
  • 对于异步操作,用 Future 等待结果或者使用 CountDownLatch 通知

测试粒度

  • 测试尽量小,执行速度快
  • 一次测试一个对象,出现问题容易排查
  • 测试方法应当尽量简单明了

结果验证

  • 尽量使用 Unit test 提供的 assert/fail 方法

保持独立

  • 执行顺序不确定,因此测试用例之间一定要保持完全独立,不能相互依赖。

仅测试公有接口

  • 测试类公有 API
  • 一些测试工具允许测试类私有成员,应该尽量避免测试私有成员,否则会让测试变得繁琐难以维护
  • 如果私有成员需要进行直接测试,可以考虑重构到工具类公有方法中

覆盖边界值

– 确保参数边界值均被覆盖。对于数字,测试负数、 0 、正数、最小值、最大值、 NaN (非数字)、无穷大等 – 对于字符串,测试空字符串、单字符、非 ASCII 字符串、多字节字符串等 – 对于集合类型,测试空、 1 、第一个、最后一个等 – 对于日期,测试 1 月 1 号、 2 月 29 号、 12 月 31 号等

编写反向测试

  • 刻意编写问题代码,验证鲁棒性和是否能够正确处理
  • 异常处理方法

单元测试无法证明代码正确性,一个失败的单测表明代码可能有错误,但一个成功的单测什么也不能证明。

reference