单元测试经验分享
合集下载
相关主题
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
根据人工定义的程序 的行为判断错误
静态
(分析代码)
动态 (执行代码)
需测试用例
8
从简单示例看方法选择
int Add(int a, int b) { return a-b; };
将+写成-
自动方法 (无效) 人工动态方法 (输入两个1,判断输出是否为2 )
9
人工动态测试
设定初始状态 (输入) 执行程序 判断结果是否正确?(输出)
用例与功能点具有对应关系 程序功能细化、明确化,列成“什么输 入,应产生什么输出”的形式,就是测 试用例
45
输入输出是……
输入 (读取的数据) 参数 成员变量 全局变量 外部媒体 输出 (改写的数据) 返回值 输出参数 成员变量 全局变量 外部媒体 输入要设定初始值,输出要判断结果是 否符合预期 只考虑真正需读/写的数据
第九次广州软件测试交流会
单元测试经验分享
王彤
2007-2-3
Copyright © WWW.GZTEST.NET
内容介绍
从经历谈单元测试的意义 如何测试? 由谁测试? 难于实施的原因及对策 测试工具开发 测试用例设计 提高测试效果效率的方法
3
从经历谈单元测试的意义
做与不做,反差强烈 保证局部代码质量 改良代码整体结构 回归测试降低后期测试、维护升级成本 回归测试适应频繁变化的需求 使开发过程可控
25
测试代码---测试函数
void CMyClassTester::Add_int_int() { //第一个测试用例 {CaseBegin(); //1 int i = 0; //2 int j = 0; //3 int ret = pObj->Add(i, j); //4 TestAssert(ret == 0); //5 CaseEnd(); } //6 }
26
生成这样子的代码就OK了
void CMyClassTester::Add_int_int() { //第一个测试用例 {CaseBegin(); //输入区 int ret = pObj->Add(i, j); //输出区 CaseEnd(); } }
27
更进一步……
void CMyClassTester::Add_int_int() { //第一个测试用例 {CaseBegin(); //1 int i = ; //2 int j = ; //3 int ret = pObj->Add(i, j); //4 TestAssert (ret == ); //5 CaseEnd(); } //6 }
41
什么叫一种输入?如…
char* strtrm(char* pstr); 去除字符串两边的空格 “ABCD ” (右边有空格) “AAAA” (两边无空格) NULL (空指针) 就是“等价类”
42
如何编写健壮的程序?
即使不考虑测试…… 编程时各种输入都要考虑 正常输入 (几种正常输入?) 边界输入 (几种边界输入?) 非法输入 (几种非常输入?) 就是“功能点”
CMyClass(); virtual ~CMyClass();
private: int mAge; //年龄 CString mPhase; //年龄阶段 };
24
测试代码---测试类
class CMyClassTester { CMyClass* pObj; //被测试类的对象指针
CaseBegin(); //用例初始化 CaseEnd(); //用例结束 ClassTest(); //执行本类中的所有测试函数 //各个测试函数加到此后 };
21
测试工具开发
基本功能 测试代码编写及生成 几个要点 与其他工具比较
22
测试工具基本功能
自动生成测试代码 开发成本不高 应用效益显著 (节约时间 保持思维延续性)
23
测试代码---产品类
class CMyClass { public: int Add(int i, int j); void Grow(int years)
31
要点---预期输出的判断
TestAssert(bool result, char* file=__FILE__, int line=__LINE) { if(!result) SendMsgToTool(file,line); } 测试失败时,发送文件名和行号给工具
32
要点---访问私有成员
36
测试用例设计
简单地设计测试用例
37
测试用例基本要素
设定输入
执行
判断输出
38
有限证明程序正确
假设用例本身 没有错误!
已测试的输入 可以证明正确
?
未测试的输入 可能含有错误
39
完整性问题
? ? 未测试的输入 往往不知道还 有多少……
?
?
40
好的用例
好的用例就是完整性高的用例集合 每一种可能输入都有对应用例 完整地定义了程序的行为 在用例所覆盖的范围内,任何修改引入 的错误都可以发现 (回归测试) 是“每一种输入”,非“每一个输入”
28
测试代码---成员访问
void CMyClassTester::Grow_int
{ {CaseBegin(); int years = 1; pObj->mAge = 8; pObj->Grow(years); TestAssert( pObj->mAge == 9 ); TestAssert( pObj->mPhase == "儿童" ); CaseEnd(); }
}
29
工作方式
测试工具负责生成测试代码,接收/处理 测试结果 由开发环境编译测试代码,执行测试工 程即运行测试 测试结果通过进程间通讯技术发送到工 具 测试代码相当单纯
30
要点---运行控制
void RunTest() { CMyClassTester tester; tester.Add_int_int(); } 通过修改代码来控制执行哪个测试
测试私有函数 读写私有成员 前/后置操作
友元 拷贝
33
与其他工具的比较
与xUnit比较 (效率差异)
与自动测试工具比较 (应用方式不同)
人工测 试效果 自动测 试效果
无特征错误
有特征错误
34
演示
免费工具演示
35
小结
基本功能 (自动生成测试代码) 编写规范的测试代码作为模板,用工具 生成之 可以参考现有产品
51
白盒覆盖的缺陷
void Func(int* p) 不能发现“忘记处理某些 { 特殊输入”形成的错误 *p = 0; }
特殊输入通常导致崩溃、 异常、超时,正是自动 动态测试的理想猎物
52
三步法
功能测试 (测试想到的输入) 根据未覆盖的逻辑单位,找出遗漏用例 自动生成测试用例,捕捉“忘记处理特 殊输入”形成的错误 难点在第二步,欲找出绝大多数遗漏用 例,需达到100%语句/条件/分支/路 径覆盖。
49
提高测试效果
效果就是测试用例完整性 如何衡量完整性? 如果保证完整性?
50
白盒覆盖找出遗漏用例
void Func(int* p) { if(p) { *p = 0; } else { return; } }
假如未测试空指针, 则else分支未覆盖
理想的覆盖组合: 100%语句、条件、 分支、路径覆盖
53
提高测试效率
描述程序行为 (帮助整理编程思路) (快速排除错误) 增强调试器功能 (提高调试效率)
54
应用演示
三步法 描述程序行为
55
总结
工具不是最重要的,关键在实施 不要因为缺陷而放弃 只测试底层代码,无法测试的可以跳过 不是解决所有问题的万金油
56
19
测试行为分解
行为 特点 对策 自动生成 编写 费时长,会中断、干扰 测试代码 思维
编写 桩代码
费时长,会中断、干扰 思维,可选
ห้องสมุดไป่ตู้
尽量避免
使设计明确化和细化, 设计 可能促进思维。复杂方 测试用例 法费时多,干扰思维
使用简单 方法
20
小结
对中断/干扰编程思维的本能抵制? 对策1:自动生成测试代码 对策2:避免编写桩代码 对策3:用简单方法设计测试用例 简单高效,即使不对症,也大有补益
15
测试部门的责任
能否实施,测试部门是关键 推动 培训 工具开发 完整性核查
16
小结
应由开发部门实施 解决完整性问题 (覆盖率检查) (测试部门人工检查) 测试部门是关键 (推动、培训、工具开发、复核)
17
难于实施的原因及对策
难于实施的原因 对策
18
也许是这样……
程序员工作的主题是……解决问题 思维周期 岂干扰、中断思维 学习与实践有何不同? 对策?
4
如何测试?
单元是什么? 错误分类 测试方法分类 测试方法选择 人工动态测试简述
5
单元是什么?
类? (太复杂) 函数?(简单实用)
6
代码错误
性能问题 时间性能 空间性能 功能错误 有特征 无特征
崩溃 异常 超时
行为特征
语法特征
7
测试方法
根据语法特征或行为特征判断错 误,只能发现有特征错误
自动 人工
43
功能点与等价类对应关系示例 功能点 等价类
只有左边有空格,返回删除左 左边有空格 边空格后的结果 只有右边有空格,返回删除右 右边有空格 边空格后的结果 两边都有空格,返回删除两边 两边有空格 空格后的结果
两边都没有空格,返回原串
空串,直接返回
两边无空格
空串
空指针,直接返回
空指针
44
设计用例的简单方法
46
小结
完整性高的用例集就是好用例 程序功能细化、明确化,就是测试用例
47
开发测试过程
开始编写实现代码时才生成测试代码 (函数名/参数等已确定) 完善第一个用例 将程序功能细化,用拷贝/修改的方式建 立其他用例 只调试失败的测试,使用测试代码调试 其实并没有多做什么
48
提高效果效率的方法
如何提高测试效果? 如何提高测试效率?
人工设定用例的输入和输出 其他工作可以自动化
10
小结
以函数为“单元” 分为有特征错误和无特征错误,后者占 大多数 测试方法有: 人工静态 人工动态 自动静态 自动动态 以人工动态测试为主要方法
11
由谁测试?
开发还是测试?根据成本来决定 成本对比 存在问题及解决办法
12
成本对比
事项 理解代码 边开发边测试 无需额外费时 理解代码 由测试部门测试 理解别人写的代码 难度大,耗时多 累积 需有编程经验,且 三日不写手生 须有详细设计文档 反复沟通 无
13
可测性问题 及时解决 人员要求 文档要求 沟通 促进编程 无需额外能力 有无文档均可 自行修正 提高编程效率
由测试实施的话……
成本,在三倍以上? 两个条件: 详细设计文档 足够的具有编码能力的测试员 可能的额外代价: 耽误对系统测试、性能测试的准备工作
14
由开发实施的话……
影响开发进度?(由测试做更慢) 测不出问题?(否,存在完整性问题) 解决完整性问题的方法: 覆盖率检查 测试部门核查 最佳方式:边开发边测试 (无需重复理解 代码,测试促进开发)
静态
(分析代码)
动态 (执行代码)
需测试用例
8
从简单示例看方法选择
int Add(int a, int b) { return a-b; };
将+写成-
自动方法 (无效) 人工动态方法 (输入两个1,判断输出是否为2 )
9
人工动态测试
设定初始状态 (输入) 执行程序 判断结果是否正确?(输出)
用例与功能点具有对应关系 程序功能细化、明确化,列成“什么输 入,应产生什么输出”的形式,就是测 试用例
45
输入输出是……
输入 (读取的数据) 参数 成员变量 全局变量 外部媒体 输出 (改写的数据) 返回值 输出参数 成员变量 全局变量 外部媒体 输入要设定初始值,输出要判断结果是 否符合预期 只考虑真正需读/写的数据
第九次广州软件测试交流会
单元测试经验分享
王彤
2007-2-3
Copyright © WWW.GZTEST.NET
内容介绍
从经历谈单元测试的意义 如何测试? 由谁测试? 难于实施的原因及对策 测试工具开发 测试用例设计 提高测试效果效率的方法
3
从经历谈单元测试的意义
做与不做,反差强烈 保证局部代码质量 改良代码整体结构 回归测试降低后期测试、维护升级成本 回归测试适应频繁变化的需求 使开发过程可控
25
测试代码---测试函数
void CMyClassTester::Add_int_int() { //第一个测试用例 {CaseBegin(); //1 int i = 0; //2 int j = 0; //3 int ret = pObj->Add(i, j); //4 TestAssert(ret == 0); //5 CaseEnd(); } //6 }
26
生成这样子的代码就OK了
void CMyClassTester::Add_int_int() { //第一个测试用例 {CaseBegin(); //输入区 int ret = pObj->Add(i, j); //输出区 CaseEnd(); } }
27
更进一步……
void CMyClassTester::Add_int_int() { //第一个测试用例 {CaseBegin(); //1 int i = ; //2 int j = ; //3 int ret = pObj->Add(i, j); //4 TestAssert (ret == ); //5 CaseEnd(); } //6 }
41
什么叫一种输入?如…
char* strtrm(char* pstr); 去除字符串两边的空格 “ABCD ” (右边有空格) “AAAA” (两边无空格) NULL (空指针) 就是“等价类”
42
如何编写健壮的程序?
即使不考虑测试…… 编程时各种输入都要考虑 正常输入 (几种正常输入?) 边界输入 (几种边界输入?) 非法输入 (几种非常输入?) 就是“功能点”
CMyClass(); virtual ~CMyClass();
private: int mAge; //年龄 CString mPhase; //年龄阶段 };
24
测试代码---测试类
class CMyClassTester { CMyClass* pObj; //被测试类的对象指针
CaseBegin(); //用例初始化 CaseEnd(); //用例结束 ClassTest(); //执行本类中的所有测试函数 //各个测试函数加到此后 };
21
测试工具开发
基本功能 测试代码编写及生成 几个要点 与其他工具比较
22
测试工具基本功能
自动生成测试代码 开发成本不高 应用效益显著 (节约时间 保持思维延续性)
23
测试代码---产品类
class CMyClass { public: int Add(int i, int j); void Grow(int years)
31
要点---预期输出的判断
TestAssert(bool result, char* file=__FILE__, int line=__LINE) { if(!result) SendMsgToTool(file,line); } 测试失败时,发送文件名和行号给工具
32
要点---访问私有成员
36
测试用例设计
简单地设计测试用例
37
测试用例基本要素
设定输入
执行
判断输出
38
有限证明程序正确
假设用例本身 没有错误!
已测试的输入 可以证明正确
?
未测试的输入 可能含有错误
39
完整性问题
? ? 未测试的输入 往往不知道还 有多少……
?
?
40
好的用例
好的用例就是完整性高的用例集合 每一种可能输入都有对应用例 完整地定义了程序的行为 在用例所覆盖的范围内,任何修改引入 的错误都可以发现 (回归测试) 是“每一种输入”,非“每一个输入”
28
测试代码---成员访问
void CMyClassTester::Grow_int
{ {CaseBegin(); int years = 1; pObj->mAge = 8; pObj->Grow(years); TestAssert( pObj->mAge == 9 ); TestAssert( pObj->mPhase == "儿童" ); CaseEnd(); }
}
29
工作方式
测试工具负责生成测试代码,接收/处理 测试结果 由开发环境编译测试代码,执行测试工 程即运行测试 测试结果通过进程间通讯技术发送到工 具 测试代码相当单纯
30
要点---运行控制
void RunTest() { CMyClassTester tester; tester.Add_int_int(); } 通过修改代码来控制执行哪个测试
测试私有函数 读写私有成员 前/后置操作
友元 拷贝
33
与其他工具的比较
与xUnit比较 (效率差异)
与自动测试工具比较 (应用方式不同)
人工测 试效果 自动测 试效果
无特征错误
有特征错误
34
演示
免费工具演示
35
小结
基本功能 (自动生成测试代码) 编写规范的测试代码作为模板,用工具 生成之 可以参考现有产品
51
白盒覆盖的缺陷
void Func(int* p) 不能发现“忘记处理某些 { 特殊输入”形成的错误 *p = 0; }
特殊输入通常导致崩溃、 异常、超时,正是自动 动态测试的理想猎物
52
三步法
功能测试 (测试想到的输入) 根据未覆盖的逻辑单位,找出遗漏用例 自动生成测试用例,捕捉“忘记处理特 殊输入”形成的错误 难点在第二步,欲找出绝大多数遗漏用 例,需达到100%语句/条件/分支/路 径覆盖。
49
提高测试效果
效果就是测试用例完整性 如何衡量完整性? 如果保证完整性?
50
白盒覆盖找出遗漏用例
void Func(int* p) { if(p) { *p = 0; } else { return; } }
假如未测试空指针, 则else分支未覆盖
理想的覆盖组合: 100%语句、条件、 分支、路径覆盖
53
提高测试效率
描述程序行为 (帮助整理编程思路) (快速排除错误) 增强调试器功能 (提高调试效率)
54
应用演示
三步法 描述程序行为
55
总结
工具不是最重要的,关键在实施 不要因为缺陷而放弃 只测试底层代码,无法测试的可以跳过 不是解决所有问题的万金油
56
19
测试行为分解
行为 特点 对策 自动生成 编写 费时长,会中断、干扰 测试代码 思维
编写 桩代码
费时长,会中断、干扰 思维,可选
ห้องสมุดไป่ตู้
尽量避免
使设计明确化和细化, 设计 可能促进思维。复杂方 测试用例 法费时多,干扰思维
使用简单 方法
20
小结
对中断/干扰编程思维的本能抵制? 对策1:自动生成测试代码 对策2:避免编写桩代码 对策3:用简单方法设计测试用例 简单高效,即使不对症,也大有补益
15
测试部门的责任
能否实施,测试部门是关键 推动 培训 工具开发 完整性核查
16
小结
应由开发部门实施 解决完整性问题 (覆盖率检查) (测试部门人工检查) 测试部门是关键 (推动、培训、工具开发、复核)
17
难于实施的原因及对策
难于实施的原因 对策
18
也许是这样……
程序员工作的主题是……解决问题 思维周期 岂干扰、中断思维 学习与实践有何不同? 对策?
4
如何测试?
单元是什么? 错误分类 测试方法分类 测试方法选择 人工动态测试简述
5
单元是什么?
类? (太复杂) 函数?(简单实用)
6
代码错误
性能问题 时间性能 空间性能 功能错误 有特征 无特征
崩溃 异常 超时
行为特征
语法特征
7
测试方法
根据语法特征或行为特征判断错 误,只能发现有特征错误
自动 人工
43
功能点与等价类对应关系示例 功能点 等价类
只有左边有空格,返回删除左 左边有空格 边空格后的结果 只有右边有空格,返回删除右 右边有空格 边空格后的结果 两边都有空格,返回删除两边 两边有空格 空格后的结果
两边都没有空格,返回原串
空串,直接返回
两边无空格
空串
空指针,直接返回
空指针
44
设计用例的简单方法
46
小结
完整性高的用例集就是好用例 程序功能细化、明确化,就是测试用例
47
开发测试过程
开始编写实现代码时才生成测试代码 (函数名/参数等已确定) 完善第一个用例 将程序功能细化,用拷贝/修改的方式建 立其他用例 只调试失败的测试,使用测试代码调试 其实并没有多做什么
48
提高效果效率的方法
如何提高测试效果? 如何提高测试效率?
人工设定用例的输入和输出 其他工作可以自动化
10
小结
以函数为“单元” 分为有特征错误和无特征错误,后者占 大多数 测试方法有: 人工静态 人工动态 自动静态 自动动态 以人工动态测试为主要方法
11
由谁测试?
开发还是测试?根据成本来决定 成本对比 存在问题及解决办法
12
成本对比
事项 理解代码 边开发边测试 无需额外费时 理解代码 由测试部门测试 理解别人写的代码 难度大,耗时多 累积 需有编程经验,且 三日不写手生 须有详细设计文档 反复沟通 无
13
可测性问题 及时解决 人员要求 文档要求 沟通 促进编程 无需额外能力 有无文档均可 自行修正 提高编程效率
由测试实施的话……
成本,在三倍以上? 两个条件: 详细设计文档 足够的具有编码能力的测试员 可能的额外代价: 耽误对系统测试、性能测试的准备工作
14
由开发实施的话……
影响开发进度?(由测试做更慢) 测不出问题?(否,存在完整性问题) 解决完整性问题的方法: 覆盖率检查 测试部门核查 最佳方式:边开发边测试 (无需重复理解 代码,测试促进开发)