文章目录
1. 不写单元测试的理由:
- 编写单元测试太花时间了 ——》在编写实现代码的时候,同时编写测试代码
- 运行测试的时间太长 ——》把耗时的测试与其他测试分开
- 测试代码并不是我的工作 ——》不要期望别人来清理自己的代码
- 我不清楚代码的行为,所以也就无从测试 ——》先建立一个原型,这样有助于认清需求
- 但是这些代码都能够编译通过 ——》编译器的默许只是对代码的一种非常粗略的肯定,只能验证语法是否正确,不能保证代码的行为也正确
- 公司请我来是为了写代码,而不是测试 ——》单元测试大体上是一个工具,是一个和编译器、开发环境、编辑器等处于统一位置的工具
- 如果我让测试员或者QA人员没有工作,那么我会觉得内疚 ——》单元测试只是一种针对源码的、低层次的,为程序员而设计的测试
- 我的公司并不会让我在真实系统中运行单元测试 ——》可以在自己本机运行单元测试,使用你自己的数据库,或者使用mock对象
2. junit
- 自定义断言 ——》封装公用断言
- 异常:可预测异常、不可预测异常
3. 测试哪些内容 ——》Right-BICEP
- 结果是否正确(right):如果代码能够运行正确,要怎么知道结果是正确的呢,
- 使用数据文件
- 边界条件 (boundary)——》CORRECT
- 检查反向关联(inverse) ——》用对结果反向操作的逻辑验证结果是否正确,比如检查某条记录是否成功地插入数据库,可以通过查询这条记录来验证
- 使用其他手段来实现交叉检查(cross-check) ——》计算某个结果可以使用多种算法,系统使用了其中一种,可以使用另一种算法来测试系统算法是否产生了相同的结果
- 强制产生错误条件(error-condition) ——》磁盘满了、网络连接断开、内存泄露、依赖失败、时钟出问题、系统过载、调色板颜色数量有限、显示分辨率过高或过低
- 性能特性(property) ——》随着输入尺寸慢慢变大,问题慢慢变复杂
4. 边界条件CORRECT ——》需要好好回答的根本问题是:还会有什么错
- 一致性(conformance) ——》准备数据是否与你期望的结构一致,是否缺头少尾
- 有序性(ordering) ——》需要考虑数据的顺序,或者是一个数据集中某一数据的位置
- 区间性(range) ——》对于一个变量,它所属类型的取值范围可能比你需要或想要的更加宽广,例如整形、负值、大于最大值、小于最小值等
- 引用/耦合性(reference) ——》程序引用了哪些位于程序之外的事务或外部依赖,程序运行还需要哪些其他条件
(1) 前条件:系统必须处于什么状态下该方法才能运行
(2) 后条件:你的方法将会保证哪些状态发生- 存在性(existence) ——》对于你传入或者维护的值,先询问自己如果值不存在,当你期望它们存在的时候它们可能并不存在,方法的行为将会怎样
- 基数性(cardinality) ——》“0-1-n ”原则
- 时间性(time) ——》相对时间,绝对时间,并发访问与同步访问
习题:
- 一个简单的栈类
(1)对于全新的栈,isEmpty应该为true,pop与top应该抛StackEmptyException异常
(2) 如果item为null,栈应该不变,如果item为“”,栈插入一条控制符传数据
(3) 如果item非常长,到达string的最大长度,栈正确插入,或者超出,栈插入异常。
(4) 对于空栈,执行了push后再执行pop,返回正常,再执行top,返回StackEmptyException异常。对于空栈,执行了push后再执行top,再执行pop都返回正常。对于有一个值的栈,执行了push后再执行pop,再执行top,或者执行了top后执行pop,都返回正常。对于一个有一个值的栈,执行pop之后执行top,pop正常,top抛StackEmptyException异常。对于有多个值的栈,单个执行的顺序都会返回正常。×每个方法都是独立的
(5) 连续压入push,每次调用top返回字符串,而isEmpty返回false。连续压入push,调用pop,再调用top,再调用isempty,如果是空栈执行,则top抛空异常,isEmpty为true。
- 一个购物车
(1) 对于一个空购物车,itemCount为0,iterator为空集合,addItems时,item为null、为不存在的商品,或者quantity为-n、-1、0、1、n,为负时抛NegativeCountException,为正时如果之前的count为负也有可能抛NegativeCountException。deleteItems抛NosuchItemException异常。
2. 如果quantity参数超过原有数量,与原有数量相等,quantity=0,或者为负,或者item不存在购物车里,
3. 如果为购物车为空,购物车某样产品数量为0,某样产品数量为负,itemcount返回值
4. 购物车为空是iterator,购物车只有一个商品时,购物车只有一种商品多个时,购物车有多种商品都只有一个时,购物车有多种商品多个时。
- 一个传真调度程序
1. 电话号码长度超过12,中间没有第一个或第二个“-”号或都没有,格式不是前三、中间三、后四的格式,电话号码为null或为“”,x不为2~9之间的数,xn不是数字
2. filename为null或为“”,
- 一个刺绣用的自动缝纫机
1. 没有需要缝纫的工件,工件大小比缝纫机大,工件大小为0
2. width<1,width=1,width>1。height<1,height=1,height>1
3. tablesize为null
- 一个音频/视频的位移编辑器
1. 快进快退时位于bot或eot或之间一个位置,快进快退的秒数为0,为负,或者超过当前位置到bot(eot)的距离,应该回到bot(eot)
2. 获取当前位置时位于bot,eot或之间某个位置
3. 标记时位于bot,eot或之间某个位置,name为null标记失败,那么为“”标记失败,那么重复标记失败
4. gotomark时name为null,name为“”,name不存在
5. 使用mock对象
- 简单的替换 ——》使用“替身”替换真实对象
- Mock对象 ——》真实对象在调试期的替代品
- 测试Servlet ——》使用MockHttpServletRequest和MockHttpServletResponse模拟request与response
- Easy Mock对象 ——》指定要mock的接口中的哪个方法被调用是允许的,它的返回值是什么
- 习题 ——》根据测试写mock实现
6. 好的测试所具有的品质 ——》A-TRIP
- 自动化(Automatic)——》单元测试需要能够被自动地运行,包含:调用测试自动化与检查结果自动化
- 彻底的(Thorough)——》测试所有可能会出现的情况,代码覆盖率或只测试最可能的情况:边界条件、残缺和畸形的数据等。
(1) 覆盖率工具:nounit,quilt,clover- 可重复(Repeatable)——》每个测试应当每次产生相同的结果
- 独立的(Independent)——》每个测试独立于环境和其他测试
- 专业的(Professional)——》使用和产品代码相同的专业水准来编写和维护测试代码,在测试代码中,针对好设计的所有普遍规则 —— 维护封装、采用DRY原则、降低耦合等,都必须如在产品代码中那样得到遵循
- 对测试进行测试 ——》修正bug的同时改进测试,通过引入bug来证明测试
(1) 如何修正bug
(2) spring the trap(自设圈套)
7. 在项目中进行测试
- 把测试代码放哪儿
(1) 同一目录
(2) 子目录
(3) 并行树- 测试的礼貌
- 测试的频率
(1) 编写新的函数
(2) 修正bug
(3) 每次成功编译之后
(4) 每次对版本控制的签入
(5) 持续不断地- 测试与遗留代码
- 测试和评审
8. 设计话题
- 面向测试的设计
- 为测试而重构
- 测试类的不变性
(1) 结构化
(2) 数学不变性
(3) 数据一致性- 测试驱动的设计——》测试先行
(1) 练习
9. Gotchas
- 只要代码能工作就可以
- 冒烟测试——》不能只冒烟
- 请让我的机器来运行——》所有机器都应该能运行
- 浮点数问题——》精度
- 测试耗费的时间太多了
- 测试总是失败——》耦合太高
- 在某些机器上测试失败——》环境问题:机器、系统、版本、运行速度
- 我的main没有被运行 ——》junit不会运行类中的main方法
10. 资源
- easy-mock:http://www.easymock.org
- junit:http://junit.org
- junitPerf:http://www.clarkware.com
- MockObject:www.mockobjects.com
- Nounit:nounit.sourceforge.net
- quilt:quilt.sourceforge.net