1.软件测试
测试是提高软件质量的重要方法。一个好的测试要有以下特点:能发现错误、不冗余、最佳特性、不复杂也不简单。但是再好的测试也存在缺陷。
测试大致可分为几个阶段:
单元测试(Unit Testing): 测试部分代码的功能
集成测试(Integration Testing): 多个类、包、组件和子系统组合的测试
系统测试(System Testing): 测试一个完全集成的系统,以验证该系统满足其要求,从而在其最终配置中执行软件
当然,还有其他的其他测试类型:安装测试、兼容性测试、烟雾和理智测试等等
静态测试(Static Testing): 在没有实际执行程序的情况下执行,例如检查代码的语法等等
动态测试(Dynamic Testing): 使用一组给定的测试用例来执行编程代码
注意! 测试(Testing)与调试(Debugging)的不同。测试(Testing)是发现代码是否存在错误,调试(Debugging)是已知有错误,去找到错误代码并消除错误。
白盒测试(White-box Testing): 源代码已知
黑盒测试(Black-box Testing): 源代码未知
想要写出好的代码测试是很困难的。首先,不可能列出所有的测试用例,因为样本数量巨大;其次,随机碰运气也很难,除非你的程序全是错误,或者你欧的一批;最后,和其他自然科学的规律性不同,程序出错不具有规律,因此很难通过基于样本的统计数据对程序进行测试。
Not only “make it fail”, but also “fail fast”.
2.测试用例
测试用例 = 输入 + 执行条件 + 期望结果
一个好的测试用例具备以下特点:
- 最可能发现错误
- 不重复、不冗余
- 最有效
- 既不简单也不复杂
3.测试优先编程
测试优先编程(Test-first Programming),即先写测试,再根据测试写源代码。
流程:
- 根据功能写规约(Spec)
- 写出符合规约的测试用例
- 最后写源代码,并且不断测试修改,直到代码通过测试
优点:
- 可以帮助你更好的了解规约,同时也能帮你提前发现可能存在于规约中的错误
- 节省大量的时间(相比于后写测试)
- 可确保程序的可测试性
- 确保每个程序特征的测试都撰写完成
规约(Specification)
函数的规约是对函数行为的描述,包含以下三项:
1.参数类型
2.返回值的类型
3.约束和它们之间的关系。
4.单元测试
单元测试(Unit Testing)是针对软件的最小单元模型,隔离各个模块开展测试,能更好的定位错误。
注意事项:
- 对单元接口进行测试,即测试输入输出
- 确保在进行测试的过程中,数据前后的一致性,不发生变化
- 所有语句至少执行一次
- 对边界条件进行测试
- 最后,测试所有错误处理路径
因为构件不是一个独立的程序,所以通常要为每个单元测试开发驱动模块和桩模块
驱动模块: 类似一个“主程序”,它接受测试用例数据,将这些数据传递给待测试的构件,并打印结果
桩模块: 模拟被测试的模块所调用的模块,与主模块相连,接受或传递被测模块的数据
5.使用JUnit进行自动单元测试
JUnit是一个Java语言的单元测试框架
在JUnit中,每个测试用例前都加上 @Test 标签
单元测试方法通常包含一个或多个对被测试模块的调用,然后使用assertEquals、assertTrue和assertFalse等断言方法检查结果
JUnit断言方法中的参数顺序:一般来说,第一个参数是期望结果(expected result);第二个参数是实际执行结果(actual result)
测试类可以包含任意数量的@Test方法,当您使用JUnit运行测试类时,这些方法是独立运行的。哪怕有些方法并不能通过测试
注意!在IDE中,Test的文件结构应该和源码的文件结构一样。
6.黑盒测试
黑盒测试(Black-box testing):用于检查代码的功能,不关心内部实现细节
黑盒测试能找到以下类型的错误:
- 功能不正确或缺失
- 界面错误
- 数据结构或外部数据库访问错误
- 行为或性能错误
- 初始化和终止错误
6.1 通过划分选择测试用例
等价类划分:将被测试函数的输入域划分为等价类,从等价类中选择测试样例
通过等价类选出最具代表性的测试用例,从而大幅降低测试用例数量
6.2 包含边界的划分
边界值分析(BVA): 选择边界值测试用例,一种补充等价划分的测试用例设计技术
大量的错误发生在边界。
覆盖划分的两个极端:
- 笛卡尔积: 多个划分维度上的多个取值,要组合起来,每个组合都要有一个用例
测试完备,但用例数量多,测试代价高 - 覆盖每个取值:每个维度的每个取值至少被1个测试用例覆 盖一次即可
测试用例少,代价低,但测试覆盖度未必高
7.白盒测试
白盒测试(Whitebox testing)要考虑内部实现细节
通过代码的执行路径设计测试用例,白盒测试一般较早执行
使用白盒测试方法,你要保证:
- 确保模块内的所有独立路径至少已执行一次
- 在正确和错误的方面做出所有合乎逻辑的决定,
- 在其边界和操作边界内执行所有循环,
- 使用内部数据结构以确保其有效性
典型的白盒测试方法称为“独立/基路径测试”
独立/基本路径测试:对程序所有执行路径进行等价类划分,找出有代表性的最简单的路径(例如循环只需执行1次),设计测试用例使每一条基本路径被至少覆盖1次
8.覆盖率的测试
测试应该考虑测试用例的代码覆盖率(Code coverage)
代码覆盖率高,说明未检测到的错误的比率更低,但需要更多的测试用例
以下有几种代码覆盖率:
- 函数覆盖
- 语句覆盖
- 分支覆盖
- 条件覆盖
- 路径覆盖
测试效果:路径覆盖 > 分支覆盖 > 语句覆盖,但是越强就需要更多的测试用例。老孙权了。 再但是,100%的路径覆盖是不可行的,需要指数大小的测试套件来实现
最彻底的白盒方法是覆盖程序中的每一条路径,但是 几乎不可能执行每条路径。举个栗子,一个程序包含一个需要执行20次的循环。它包括520个不同的执行路径。假设测试每条路径需要1ms,则完成所有路径的测试需要3170年
实用工具:EclEmma(最新版Eclipse自带)、一般IDEA中也是自带覆盖率检测的运行选项的。没有的话,Coverage插件多的一批
9.自动化测试和回归测试
自动化测试(Automated testing)意味着运行测试并自动检查结果,就比如JUnit就是一个自动化的测试框架
回归测试(Regression testing):代码经过了修改就要再次进行一遍所有的测试
10.记录测试策略
单元测试策略是ADT设计的补充文档
与测试优先编程的思想相一致,建议写下设计测试用例所依据的测试策略(如分区和边界)
目的: 在代码评审过程中,其他人可以理解你的测试,并评判你的测试是否足够充分