单元测试的好处是众所周知的, 它可以提早发现bug, 提高开发过程中的项目质量, 完善代码的设计等. 所以大部分人是认同单元测试的作用, 也都同意它的必要性. 但是真正在实施或落地单元测试的时候又会有各种各样的阻力.理由也是多种多样的, 例如费时, 测试代码不是我的工作, 代码能编译通过为什么还要写单元测试等.
费时
这应该是绝大多数人不做单元测试的理由, 很多人认为测试的工作是提测之后才开始的工作.其实正确得做单元测试是可以达到节省时间的目的, 虽然花了一些时间在编写和维护测试代码上, 但是考虑平时debug时间, 定位bug时间和测试同事沟通bug时间, 甚至是改了老bug出现新bug的情况, 综合这些因素做单元测试可以在保证质量的基础之上节省开发的时间, 并且能够提高对自己代码的信心.
测试不是我的工作
虽然测试不是开发的工作, 但是当开发放出很难维护有Bug的功能给测试后, 到项目验收期间所消耗的时间和经历得不偿失. 开发应当把高质量的输出作为自己的责任. 而不是自顾自的堆代码.
代码能够编译通过
这是一个很拙略的借口, 代码编译通过只能验证语法是否正确, 并不能保证程序的行为是否正确.
单元测试实施起来之后应该作为项目质量的基石, 稳定的基石才能确保项目从下层到上层乃至真个项目的稳定.
单元测试的职责
单元测试的核心思想是验证代码的行为和预期一致, 并且测试代码应该很简洁清晰的, 不管性能上差些或者行为上啰嗦一些也要让他具有良好的可读性.写单元测试时跟自己的代码进行如下交互.
- 它的行为和我的期望一致吗?
- 它的行为一直和我的期望一致吗? (多路径, 多条件覆盖)
- 单元测试也是文档.
单元测试的流程
如何设计单元测试的流程? 从比较高纬度看单元测试必须包含以下四个流程.
- 准备测试所需要的各种条件, 例如创建出必要的对象, 资源模拟环境等.
- 调用要测试的方法.
- 验证被测试方法的行为并和期望对比
- 释放各种资源
验证测试结果
如何通过验证测试结果来挖掘bug? 可以从下面几个角度, 特别是边界条件来探测代码是否有bug.
- Right 运行结果是否正确
Boundary 是否所有的边界条件都正确
大多数的Bug都是出现在边界条件附近, 可以通过CORRECT辅助测试边界条件.
- Conformance 一致性. 验证期望与结果是否一致.
- Ordering 有序性. 如果数据是有序的则可以通过乱序反序等条件试探边界.
- Range 区间性. 数据本身属性的区间比实际需要的更加宽广时, 验证越界情况.
- Reference 引用依赖性. 在代码有引用或依赖外部状态时相当于前条件, 检测代码在外部状态异常情况下的准确性.以及代码运行后产生的间接结果即后条件是否正常.
- Existence 存在性. 当网络, 文件, 硬件设备等环境的任何事物不存在时, 要测试这种不存在的情况.(没有get到跟Reference的区别)
- Cardinality
- Time
Inverse 反向关联
- Cross-check 交叉检查结果
- Error 是否可以强制触发错误条件
- Performance 是否满足性能要求
验证Bug修复流程
发现Bug之后修复Bug的流程
- 发现bug
- 编写一个失败的测试用例证明bug的存在
- 修正bug, 并通过测试
- 验证所有的测试都能通过
单元测试陷阱
单元测试应该是让开发的工作更加轻松, 但是如果使用不当也会浪费大量的时间, 会花费更多的时间在维护和调试测试代码上, 进而影响整个项目的开发周期. 为了避免这些问题发生, 可以在做测试时遵循一些简单的规则, 缩写是A-PTRIP.
- Automatic 自动化. 单元测试调用和检验结果的自动化
- Thorough 彻底的. 根据场景尽可能覆盖更深更广的单元测试, 通常情况下单元测试的数量和问题数量成反比.
- Repeatable 可重复. 基于独立性确保测试能够以任意顺序重复得执行, 并且结果应该保持一致.
- Independent 独立的. 测试应该是简洁精炼的, 应该有很强的针对性, 能够独立于环境和其他的测试.
- Professional 专业的. 测试代码也要准守基本的规则, 维护封装解耦等.
Pragmatic Unit Testing in Java with JUnit
转载请注明出处:http://blog.csdn.net/l2show/article/details/71259203