总览
这是本相对简单的书,书中采用的 JUnit 的版本也是旧的,但是在新的 JUnit4 下稍做修改依然可以运行。重要的是通过这本书了解 JUnit 在 Java 的单元测试中是如何使用的。原书代码只练习了一部分,一个原因是内容有点旧,新出的许多工具不需要旧的方式操作;一个原因是现在不做开发,不太需要深入理解细节。
第 2 章 首个单元测试
计划你的测试 : 测试不是无中生有的,也不是意想天开的。是根据需要一点点添加的,帮助自己尽早地发现思考上的误区。参看这章给出的例子,原来理所当然正确的,结果不一定是正确的。
第 3 章 使用 JUnit 编写测试
3.1 构建单元测试
测试代码必须要做的几件事情 :
- 准备测试的条件 ( 创建对象、分配资源等等 )
- 调用测试的方法
- 验证测试方法的行为与期望是否相符
- 测试结束后清理现场 ( 释放资源等等 )
3.2 JUnit 的各种断言
断言 : JUnit 提供的辅助函数,帮助你确认被测试函数是否正确运行。
后面还介绍了 ( 3.5 JUnit 的自定义断言 )
3.3 JUnit 框架
这章是基于 JUnit3.x 写的,建议了解就可以了,因为 JUnit4 的变化较大,使用也更方便直观,因此直接参考 JUnit4 的帮助。
框架运行顺序 | 对应于标签 |
---|---|
setUpBeforeClass() | @BeforeClass |
setUp() | @Before |
testMethod1() | |
tearDown() | @After |
setUp() | @Before |
testMethod2() | |
tearDown() | @After |
tearDownAfterClass() | @AfterClass |
4. 测试什么
6 个需要测试的地方 ( Right-BICEP ) :
- Right : 结果是否正确 ( Right ) ;
- B : 边界 ( Boundary ) 条件是否正确(CORRECT);参考第 5 章
- I : 能否检查反向 ( Inverse ) 关系;
- C : 进行交叉检查 ( Cross-Check ) 的其他手段;
- E : 强制错误 ( Error ) 条件发生;使用 Mock 对象实现,参考第 6 章
- P : 满足性能 ( Performance ) 的要求。
测试内容较多时,可以使用测试数据文件进行准备。但是使用文件后就没有测试代码看起来那么直观了,因此除非测试内容非常复杂,否则没有必要采用这样的方式。并且如果测试文件出现错误 ( 作者书中就出现了数据错误 ) ,还会导致测试不通过,增加了维护的成本。
5.CORRECT ( 正确的 ) 边界条件
- 一致性 ( Conformance ) : 值是否符合预期的格式;
- 有序性 ( Ordering ) : 一组值是否符合对排序的要求 ( 有序性、无序性 ) ;
- 区间性 ( Range ) : 值是否在合理取值范围内 ( 在最小值与最大值之间 ) ;
- 引用 ( Reference ) -耦合性 : 代码是否引用了不受代码本身直接控制的外部因素;
- 存在性 ( Existence ) : 值是否存在 ( 例如 : 非 NULL,非零,包含于某个集合等等 )
- 基数性 ( Cardinality ) : 是否恰好有足够的值; ( 也称为集合的势,即集合里面包含的元素个数 )
- 时间性 ( Time ) -绝对时间和相对时间 : 所有的事情是否按照顺序发生?是否在正确的时间发生?是否及时发生?
6. 使用 Mock 对象
Mock 对象解决的问题 :
- 真实对象具有不可确定的行为 ( 如 : 股票行情 ) ;
- 真实对象很难被创建;
- 真实对象的某些行为很难被触发 ( 如 : 网络错误 ) ;
- 真实对象令程序的运行速度很慢;
- 真实对象有用户界面或者就是用户界面;
- 真实对象需要被询问它是如何被调用的 ( 如 : 验证某个回调函数是否被调用 ) ;
- 真实对象实际上不存在 ( 如 : 其他开发小组的接口、或者某个没有的硬件产品 ) 。
Mock 对象解决的步骤 :
- 使用一个接口来描述这个对象;
- 为产品代码实现这个接口;
- 以测试为目的,在 Mock 对象中实现这个接口。
注 : 这里的 Mock 不是网上已经形成框架的 Mock 工具,是 Mock 的实现原理。作者推荐的 Mock 工具是 EasyMock。其他的 Mock 工具可以参考《使用 Mock 进行单元测试》
7. 好的测试所具有的品质(A-TRIP)
- 自动化 ( Automatic ) : 自动化地调用测试和检查结果;常用的持续集成工具
- 彻底的 ( Thorough ) : 测试了所有需求关注的情况;常用的代码覆盖工具
- 可重复 ( Repeatable ) : 每个测试应该独立于其他所有的测试,还必须独立于环境,从而可以重复地执行,并且产生相同的结果。
- 独立的 ( Independent ) : 确保一个函数只针对一样测试,并且这个测试不依赖于其他测试。
- 专业的 ( Professional ) : 测试代码应该与产品代码的编码风格和编写质量相同
如何确保测试代码是正确的呢?
- 对产品代码中的 Bug 进行修改的时候也改进测试代码; ( 因为这个 Bug 是测试代码没有发现的 )
- 在产品代码中引入 Bug 来验证测试代码的正确性。 ( 确保可能会发生的错误被测试代码捕捉到了 )
8. 在项目中进行测试
- 把测试代码与产品代码放在一个目录下;
- 与别人共享代码的时候,需要确保你的代码可以通过所有测试;
- 测试的时间点 :
- 编写新的函数;
- 修正 Bug;
- 每次成功编译之后;
- 每次对版本控制的提交;
- 持续不断地由专门的机器来运行完整的构建和测试。
- 测试别人的项目代码 : 其实就是维护别人的项目绝对是个大问题,同时也是个必须面对的问题。需要理性的态度 ( 不批评别人的代码 ) 、冷静的手段 ( 不随便修改别人的代码 ) 、持久的耐心 ( 先从测试代码开始,慢慢重构项目代码,使之重新回到健康状态 ) 、真正的智慧 ( 知道什么样的项目应该达到什么样的目标,不执着于重构成一个完美的状态,也不简单放弃随之自生自灭。 )
- 测试与评审 : 三个臭皮匠顶个诸葛亮,放下自我的执着,接纳各种不同的意见,才能做出令自己满意的项目。
9. 设计话题
- 面向测试的设计 : 不方便测试的设计不是好的设计;说明设计过于僵化或者臃肿,需要简化或者修改使之更利用未来的扩展和维护。
- 面向测试的重构 : 不方便测试的代码不是好的代码;说明业务混杂在一起,无法实现一个函数只针对一样测试,需要修改设计使业务分离。
- 测试类的不变性 : 就是对类的断言必须为真。
- 有序性。例如 : sorted list 类的不变性就是无论发生什么,结果都应该是有序的。
- 结构化。例如 : 订单系统中每个条目必须属于一个订单,一个订单拥有一个或多个条目。
- 数学不变性。例如 : 银行账号的的借贷必须平衡。
- 数据一致性。例如 : 商品总数=库存数+销售数。
- 测试驱动的设计。使你作为产品代码的用户在编码,而不是产品开发者在编码,开发结果更能反应用户的需求。
- 测试无效的参数。当你作为产品代码的用户时,你才能真正确定哪些责任应该你来承担,而哪些是不需要的。例如 : 无效的参数应该由哪个函数来承担检查责任呢?