单元测试系列一:单元测试之道

1. 不写单元测试的理由:

  1. 编写单元测试太花时间了 ——》在编写实现代码的时候,同时编写测试代码
  2. 运行测试的时间太长 ——》把耗时的测试与其他测试分开
  3. 测试代码并不是我的工作 ——》不要期望别人来清理自己的代码
  4. 我不清楚代码的行为,所以也就无从测试 ——》先建立一个原型,这样有助于认清需求
  5. 但是这些代码都能够编译通过 ——》编译器的默许只是对代码的一种非常粗略的肯定,只能验证语法是否正确,不能保证代码的行为也正确
  6. 公司请我来是为了写代码,而不是测试 ——》单元测试大体上是一个工具,是一个和编译器、开发环境、编辑器等处于统一位置的工具
  7. 如果我让测试员或者QA人员没有工作,那么我会觉得内疚 ——》单元测试只是一种针对源码的、低层次的,为程序员而设计的测试
  8. 我的公司并不会让我在真实系统中运行单元测试 ——》可以在自己本机运行单元测试,使用你自己的数据库,或者使用mock对象

2. junit

  1. 自定义断言 ——》封装公用断言
  2. 异常:可预测异常、不可预测异常

3. 测试哪些内容 ——》Right-BICEP

  1. 结果是否正确(right):如果代码能够运行正确,要怎么知道结果是正确的呢,
  2. 使用数据文件
  3. 边界条件 (boundary)——》CORRECT
  4. 检查反向关联(inverse) ——》用对结果反向操作的逻辑验证结果是否正确,比如检查某条记录是否成功地插入数据库,可以通过查询这条记录来验证
  5. 使用其他手段来实现交叉检查(cross-check) ——》计算某个结果可以使用多种算法,系统使用了其中一种,可以使用另一种算法来测试系统算法是否产生了相同的结果
  6. 强制产生错误条件(error-condition) ——》磁盘满了、网络连接断开、内存泄露、依赖失败、时钟出问题、系统过载、调色板颜色数量有限、显示分辨率过高或过低
  7. 性能特性(property) ——》随着输入尺寸慢慢变大,问题慢慢变复杂

4. 边界条件CORRECT ——》需要好好回答的根本问题是:还会有什么错

  1. 一致性(conformance) ——》准备数据是否与你期望的结构一致,是否缺头少尾
  2. 有序性(ordering) ——》需要考虑数据的顺序,或者是一个数据集中某一数据的位置
  3. 区间性(range) ——》对于一个变量,它所属类型的取值范围可能比你需要或想要的更加宽广,例如整形、负值、大于最大值、小于最小值等
  4. 引用/耦合性(reference) ——》程序引用了哪些位于程序之外的事务或外部依赖,程序运行还需要哪些其他条件
    (1) 前条件:系统必须处于什么状态下该方法才能运行
    (2) 后条件:你的方法将会保证哪些状态发生
  5. 存在性(existence) ——》对于你传入或者维护的值,先询问自己如果值不存在,当你期望它们存在的时候它们可能并不存在,方法的行为将会怎样
  6. 基数性(cardinality) ——》“0-1-n ”原则
  7. 时间性(time) ——》相对时间,绝对时间,并发访问与同步访问

习题:

  1. 一个简单的栈类
    (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。

  2. 一个购物车
    (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,购物车只有一个商品时,购物车只有一种商品多个时,购物车有多种商品都只有一个时,购物车有多种商品多个时。

  3. 一个传真调度程序
    1. 电话号码长度超过12,中间没有第一个或第二个“-”号或都没有,格式不是前三、中间三、后四的格式,电话号码为null或为“”,x不为2~9之间的数,xn不是数字
    2. filename为null或为“”,

  4. 一个刺绣用的自动缝纫机
    1. 没有需要缝纫的工件,工件大小比缝纫机大,工件大小为0
    2. width<1,width=1,width>1。height<1,height=1,height>1
    3. tablesize为null

  5. 一个音频/视频的位移编辑器
    1. 快进快退时位于bot或eot或之间一个位置,快进快退的秒数为0,为负,或者超过当前位置到bot(eot)的距离,应该回到bot(eot)
    2. 获取当前位置时位于bot,eot或之间某个位置
    3. 标记时位于bot,eot或之间某个位置,name为null标记失败,那么为“”标记失败,那么重复标记失败
    4. gotomark时name为null,name为“”,name不存在

5. 使用mock对象

  1. 简单的替换 ——》使用“替身”替换真实对象
  2. Mock对象 ——》真实对象在调试期的替代品
  3. 测试Servlet ——》使用MockHttpServletRequest和MockHttpServletResponse模拟request与response
  4. Easy Mock对象 ——》指定要mock的接口中的哪个方法被调用是允许的,它的返回值是什么
  5. 习题 ——》根据测试写mock实现

6. 好的测试所具有的品质 ——》A-TRIP

  1. 自动化(Automatic)——》单元测试需要能够被自动地运行,包含:调用测试自动化与检查结果自动化
  2. 彻底的(Thorough)——》测试所有可能会出现的情况,代码覆盖率或只测试最可能的情况:边界条件、残缺和畸形的数据等。
    (1) 覆盖率工具:nounit,quilt,clover
  3. 可重复(Repeatable)——》每个测试应当每次产生相同的结果
  4. 独立的(Independent)——》每个测试独立于环境和其他测试
  5. 专业的(Professional)——》使用和产品代码相同的专业水准来编写和维护测试代码,在测试代码中,针对好设计的所有普遍规则 —— 维护封装、采用DRY原则、降低耦合等,都必须如在产品代码中那样得到遵循
  6. 对测试进行测试 ——》修正bug的同时改进测试,通过引入bug来证明测试
    (1) 如何修正bug
    (2) spring the trap(自设圈套)

7. 在项目中进行测试

  1. 把测试代码放哪儿
    (1) 同一目录
    (2) 子目录
    (3) 并行树
  2. 测试的礼貌
  3. 测试的频率
    (1) 编写新的函数
    (2) 修正bug
    (3) 每次成功编译之后
    (4) 每次对版本控制的签入
    (5) 持续不断地
  4. 测试与遗留代码
  5. 测试和评审

8. 设计话题

  1. 面向测试的设计
  2. 为测试而重构
  3. 测试类的不变性
    (1) 结构化
    (2) 数学不变性
    (3) 数据一致性
  4. 测试驱动的设计——》测试先行
    (1) 练习

9. Gotchas

  1. 只要代码能工作就可以
  2. 冒烟测试——》不能只冒烟
  3. 请让我的机器来运行——》所有机器都应该能运行
  4. 浮点数问题——》精度
  5. 测试耗费的时间太多了
  6. 测试总是失败——》耦合太高
  7. 在某些机器上测试失败——》环境问题:机器、系统、版本、运行速度
  8. 我的main没有被运行 ——》junit不会运行类中的main方法

10. 资源

  1. easy-mock:http://www.easymock.org
  2. junit:http://junit.org
  3. junitPerf:http://www.clarkware.com
  4. MockObject:www.mockobjects.com
  5. Nounit:nounit.sourceforge.net
  6. quilt:quilt.sourceforge.net
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
完整的中文版《单元测试之道C#版》。单元测试不但会使你的工作完成得更轻松,而且会令你的设计变得更好,甚至大大减少你花在调试上面的时间。 在我们上面的小故事里面,Pat 因为假设底层的代码是正确无误的而卷入麻烦之中,先是高层代码中使用了底层代码;然后这些高层代码又被更高层的代码所使用,如此往复。在对这些代码的行为没有任何信心的前提下,Pat 等于是在假设上面用竖立卡片堆砌了一间房子——只要将下面卡片轻轻移动,整间房子就会轰然倒塌。 当基本的底层代码不再可靠时,那么必需的改动就无法只局限在底层。虽然你可以修正底层的问题,但是这些对底层代码的修改必然会影响到高层代码,于是高层代码也连带地需要修改;以此递推,就很可能会动到更高层的代码。于是,一个对底层代码的修正,可能会导致对几乎所有代码的一连串改动,从而使修改越来越多,也越来越复杂。于是,整间由卡片堆成的房子就由此倒塌,从而使整个项目也以失败告终。 Pat 总是说:“这怎么可能呢?”或者“我实在想不明白为什么会这样”。如果你发现自己有时候也会有这种想法,那么这通常是你对自己的代码还缺乏足够信心的表现——你并不能确认哪些是工作正常的而哪些不是。 为了获得Dale 所具有的那种对代码的信心,你需要“询问”代码究竟做了什么,并检查所产生的结果是否确实和你所期望的一致。 这个简单的想法描述了单元测试的核心内涵:这个简单有效的技术就是为了令代码变得更加完美。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值