• 1. 软件单元测试
  • 2. 课程内容第一章 详细设计伪码简介 第二章 单元测试理论 第三章 逻辑覆盖率 第四章 单元测试用例设计 第五章 单元测试开展整体思路 第六章 单元测试环境搭建和单元测试执行
  • 3. 1.1 伪码介绍1.1.1 伪码的概念 伪码使用自然语言、数学符号和一组关键字来描述算法。可以根据详细设计人员的语言使用习惯选择相应的自然语言,比如中文或者英文。 1.1.2 伪码的组成要素: 简单数据类型 语句 复杂数据类型 注释 函数调用 具体参见《伪码编写规范.doc》
  • 4. 简单数据类型可以在伪码中使用的简单数据类型如下: Integer 表示整型 Real 表示实型 Boolean 表示布尔型 Character 表示字符型 String 表示字符串型 例: Integer iCodeFlag=RET_FAIL; Integer iCodeLineLen=0; Integer iLoop=0; String szTmpCommLine; String szTmpCodeLine;
  • 5. 伪码中的语句条件语句 循环语句 分支语句 变量定义 指针定义和指针嵌套 输入与输出 操作运算符
  • 6. 条件语句 IF (condition1) { ...... } Else if (condition2) { ...... } Else...
  • 7. 循环语句While (condition) { ...... } For Var = StartValue To EndValue Step N { ...... }
  • 8. 分支语句Switch xxx { Case yyy: ...... Break; Default: Break; }
  • 9. 变量定义Variable type integer total; real income; integer number_of_people;
  • 10. 输入与输出Get "variable", "variable", ... Display "variable", "variable", ...
  • 11. 操作运算符+ add 加 - subtract 减 * multiply 乘 / divide 除 = assign 赋值
  • 12. 1.2.3 复杂数据类型类定义 结构体定义
  • 13. 类定义 Class classname { Attributes private : public : protected : Methods Method1 (para1, para2, ...) { } Method2 (para1, para2, ...) {} }
  • 14. 结构体定义structure example_struct { integer total; real income; integer number_of_people; }
  • 15. 1.2.4 注释注释使用/* 和*/ 符号括起。
  • 16. 1.2.5 函数调用调用函数 call with , 返回函数返回值 Return 得到函数返回值 return_value = call routin-name with ,
  • 17. 函数定义函数定义应该包括如下内容 /************************************************* Function: // 函数名称 Description: // 函数功能、性能等的描述 Calls: // 被本函数调用的函数清单 Called By: // 调用本函数的函数清单 Table Accessed: // 被访问的表(此项仅对于牵扯到数据库操作的程序) Table Updated: // 被修改的表(此项仅对于牵扯到数据库操作的程序) Input: // 输入参数说明,包括每个参数的作用、取值说明及参数间关系。 Output: // 对输出参数的说明。 Return: // 函数返回值的说明 Others: // 其它说明 *************************************************/
  • 18. 1.3 伪码写作注意事项伪码中的语句 伪码的风格 伪码的精炼性 伪码的细节
  • 19. 1.3.1 伪码中的语句每个语句占用一行 每个语句以一个动作或动词开始 动词后面的名词代表被操作的数据对象 例如 Open DestFile …… Create object …
  • 20. 1.3.2 伪码的风格尽量使用标准单词,避免使用太多与编程语言相关的特殊关键字 使用空行和缩进来标识程序中不同的结构和元素。 在缩进时,使用固定的缩进风格。即:如果你使用四个空格作为一次缩进, 那在程序中就一直使用四个空格 参见《伪码缩进案例.txt》 当使用英文作为自然语言时,句首的第一个字母大写。通常在整句中使用小写英文,但句首的第一个字母大写更合适一些 一旦选定了术语或者变量名,要在整个伪码中坚持下来,不要中途改变
  • 21. 1.3.3 伪码的精炼性使用自然语言并不意味着罗嗦,语句: read x 和 read from the keyboard into the variable x 伪码一样容易理解 伪码可以少输入一些字母,但不要丢失信息,例: read x 与语句 read x from file y 具有不同的含义,在前者,我们假定x是从键盘接受输入的。
  • 22. 1.3.4 伪码的细节写伪码的主要目的是使逻辑性尽可能地明确。为了做到这一点,可以忽略显而易见的错误处理过程,但是主要的错误/异常处理应当细化一下 伪码不能太细化,以至于变成了编程语言,记住最后用目标编程语言写出的程序必须包含每一个必要的细节
  • 23. 详细设计中伪码案例案例 1 伪码案例1 2 伪码案例2 3 Counter V1.0详细设计说明书.doc
  • 24. 课程内容第一章 详细设计及伪码简介 第二章 单元测试理论 第三章 逻辑覆盖率 第四章 单元测试用例设计 第五章 单元测试开展整体思路 第六章 单元测试环境搭建和单元测试执行
  • 25. 2.1 单元测试对象在传统的结构化编程语言中,比如C,要进行测试的单元一般是函数或子过程。在类似C++这样的面向对象的语言中,要进行测试的基本单元是类或类的方法。对Ada语言来说,开发人员可以选择是在独立的过程和函数,还是在Ada包的级别上进行单元测试。单元测试的原则同样被扩展到第四代语言(4GL)的开发中,在这里基本单元被典型地划分为一个菜单或显示界面。图形化开发:按钮 这里的基本单元不一定是指一个具体的函数(function或procedure)或一个类的方法(method)。在具体实现时,也可能对应的是多个程序文件中的一组函数。例如某个函数A只被函数B调用,并且函数A和函数B的代码总和在一定的范围之内,则可以考虑把A和B合并作为一个单元进行测试,但这些原则必须在单元测试计划或单元测试方案中明确说明。在多个函数的代码总行数小于200行时考虑将它们看成一个单元。在纯C语言的代码中,我们一般认为一个函数就是一个单元,这主要是为了避免开发人员和测试人员陷入不必要的单元争论当中去。同时也可以避免歧义。 A-B-C内部为顺序调用,内部无分支,并且代码规模在一定范围内,可以把A+B+C看作一个单元。——偷懒的方式,分开测更彻底更充分
  • 26. 2.2 单元测试的目的单元测试的目的: 在于发现各模块内部可能存在的各种错误,主要是基于白盒测试。 单元测试的角度: 针对文档的测试; 针对代码的测试; 针对文档和代码是否一致的测试。
  • 27. 2.3 在编码过程中引入的错误与设计不符引入的错误 由于编码出现疏漏导致错误
  • 28. 例:与设计不符引入的错误例如,设计一个函数Abs(x), 对x取绝对值: 结果在编码时 int Abs(int x) { if(x>=0) return x; else return -x; } 把x>=0写成了x<=0,就出现了与设计不相符的错误。x >= 0输入xx = - x返回xYN
  • 29. 例:由于编码出现疏漏导致错误例如,详细设计要求定义一个求平方的宏: 宏是一种定义,他定义了一个语句块,当程序编译时,编译器首先要执行一个“替换”源程序的动作,把宏引用的地方替换成宏定义的语句块,就像文本文件替换一样。这个动作术语叫“宏的展开”。 编码这样实现了: #define Squ(x) x*x … Squ(2+5)//这里本来希望结果是7*7=49,实际上成了2+5*2+5=17。 为避免这种疏漏,应注意编码规范的制定:例,在宏定义时要求参数必须使用括号() #define Squ(x) (x)*(x) 即使是这样,这个宏也还是有Bug,因为如果我这样调用: Squ(i++); , 经过这个宏以后,i被累加了两次,这绝不是我们想要的。所以,在宏的使用上还是要谨慎考虑,因为宏展开的结果是很难让人预料的。而且,虽然宏的执行很快(因为没有函数调用的开销),但宏会让源代码膨胀,使目标文件尺寸变大,(如:一个50行的宏,程序中有1000个地方用到,宏展开后会很不得了),相反不能让程序执行得更快(因为执行文件变大,运行时系统换页频繁) 不要为宏定义加分号:当宏展开后,分号也被展进去了。 与函数的区别:不是真正的封装、重用
  • 30. 2.4 单元的常见错误单元的常见错误一般出现在以下五个方面,因此这五个方面是单元测试应该关注的重点。单元测试善于发现哪些方面的问题。 代码的稳定、易读、规范、易维护、专业
  • 31. 2.4.1 单元接口被测模块接收的实参与模块的形参在个数、属性、顺序上是否匹配; 被测模块调用子模块时,它输入给子模块的实参与子模块中的形参在个数、属性、顺序上是否匹配; 输出给标准函数的参数在个数、属性、顺序上是否正确; 全局变量的定义在各个模块中是否一致;例:int a; extern float a; 是否修改了只做输入用的形式参数; 参见case1.cpp 约束条件是否通过形式参数来传送。 即降低耦合关系
  • 32. 2.4.2 局部数据结构检查不正确或不一致的数据类型说明; 例:int add(float a, float b); //函数声明 float add(float a, float b){…} //函数定义 使用尚未赋值或尚未初始化的变量; 例:int a,b,result; result=a/b; 错误的初始值或错误的缺省值; 例:char a=“hello”; 变量名拼写错误或书写错误; 例:stu_Age=20; printf(“学生年龄:%d”, stuAge;);
  • 33. 2.4.3 独立的路径运算的优先次序不正确或误解了运算的优先次序; 例: a = b + c >> 2; 不同数据类型的比较; 不正确的逻辑运算符或优先次序; “差1 错”。 即不正确的多循环或少循环一次; 例: while循环和do while循环 错误的或不可能的循环终止条件; 例:死循环 关系表达式中不正确的变量和比较符; 例:if(a==1) (中文输入模式的等号) 当遇到发散的迭代时不能终止的循环; 不适当地修改了循环变量等。
  • 34. 2.4.4 出错处理出错的描述难以理解; 例: “Error code00001”; 出错的描述不足以对错误定位和确定出错的原因; 例:“用户输入的信息错误” 显示的错误与实际的错误不符; 例:用户名输入错误,结果提示“密码输入错误”; 对错误条件的处理不正确; 例:提示“密码输入错误”,但点击确定后仍能登陆成功。 在对错误进行处理之前,错误条件已经引起系统的干预等。
  • 35. 2.4.5 边界条件边界上出现错误的是常见的。 在 n 次循环的第 n 次,取最大最小值时容易发生错误; 例: int a[10]={1,2,3,4,5,6,7,8,9,10}; for(i=1;i<=10;i++) { sum = sum + a[i] ; } 特别要注意数据流,控制流中刚好等于、大于、小于确定的比较值时出现错误的可能性。 例:运算表达式中出现> >= < <=符号时,都要考虑相应的边界值
  • 36. 2.5 单元、集成、系统测试的区别测试方法不同 单元测试属于白盒测试范畴 集成测试属于灰盒测试范畴 系统测试属于黑盒测试范畴 考察范围不同 单元测试主要测试单元内部的数据结构、逻辑控制、异常处理等 集成测试主要测试模块之间的接口和接口数据传递关系,以及模块组合后的整体功能 系统测试主要测试整个系统相对于需求的符合度 评估基准不同 单元测试的评估基准主要是逻辑覆盖率 集成测试的评估基准主要是接口覆盖率 系统测试的评估基准主要是测试用例对需求规格的覆盖率
  • 37. 2.6 单元测试环境在单元测试时,由于单元本身不是一个独立的程序,一个完整的可运行的软件系统并没有构成,所以需要设置一些辅助测试单元。辅助测试单元有两种:驱动单元和桩单元。 驱动单元(Driver):比喻:火车头,用来模拟被测试单元的上层单元,相当于被测函数的主程序,如main函数。它接收测试数据,将相关数据传送给被测单元,启动被测单元,最后再输出实测结果。当被测试单元能完成相关功能时,也可以不要驱动单元。 桩单元(Stub):用来代替被测单元工作过程中调用的子单元。桩单元桩单元被测单元驱动单元结果测试用例测试用例测试用例测试用例驱动单元和桩单元都是额外的开销,虽然在单元测试中必须编写,但并不需要作为最终的产品提供给用户。
  • 38. 2.6.1 驱动单元驱动单元:主要完成以下几个步骤: 接收测试数据,包含测试用例输入和预期输出; 把测试用例输入传送给要测试的单元,驱动被测单元执行。 将被测单元的实际输出和预期输出进行比较,得到测试结果; 将测试结果输出到指定位置;
  • 39. 2.6.2 桩单元桩单元:桩单元的功能是从测试角度模拟被被测单元所调用的其他单元。桩单元需要针对不同的输入,返回不同的期望值,模拟不同的功能。 类型:自定义函数、系统函数 桩单元模拟的单元可能是自定义函数:这些自定义函数可能尚未编写完成,为了测试被测单元,需要构造桩单元来代替它们;或者可能存在错误,会影响测试结果,给分析被测单元造成困难,因此需要构造正确无误的桩单元来达到隔离的目的。 分析:以StatCodeLine和IsCodeLine函数为例 如果测试StatCodeLine,需要提供什么辅助单元? 如果测试IsCodeLine,需要提供什么辅助单元?
  • 40. 2.7 单元测试策略一般的单元执行策略有三种: 孤立的单元测试策略(Isolation Unit Testing), 自顶向下的单元测试策略(Top Down Unit Testing)和 自底向上的单元测试策略(Bottom Up Unit Testing) 需要注意的是,在集成测试中也有自顶向下和自底向上的测试策略。 区别仅在于测试的对象不同。
  • 41. 2.7.1 孤立的测试策略方法:不考虑每个模块与其他模块之间的关系,为每个模块设计桩模块和驱动模块。每个模块进行独立的单元测试。 优点:该方法是最简单,最容易操作的。可以达到高的结构覆盖率。该方法是纯粹的单元测试。 缺点:桩函数和驱动函数工作量很大,效率低。
  • 42. 2.7.2 自顶向下的测试策略方法:先对最顶层的单元进行测试,把顶层所调用的单元做成桩模块。其次对第二层进行测试,使用上面已测试的单元做驱动模块。如此类推直到测试完所有模块。 优点:可以节省驱动函数的开发工作量,测试效率较高。 缺点:随着被测单元一个一个被加入,测试过程将变得越来越复杂,并且开发和维护的成本将增加。
  • 43. 2.7.3 自底向上测试策略方法:先对模块调用层次图上最低层的模块进行单元测试,模拟调用该模块的模块做驱动模块。然后再对上面一层做单元测试,用下面已被测试过的模块做桩模块。以此类推,直到测试完所有模块。 优点:可以节省桩函数的开发工作量,测试效率较高。 缺点:不是纯粹的单元测试,底层函数的测试质量对上层函数的测试将产生很大的影响。
  • 44. 2.7.4 混合测试策略自顶向下和自底向上的测试策略综合了集成的概念,随着单元测试的进行,可以看到系统一个初步集成的概貌,但是覆盖率会越来越难以保证,并且在每个单元测试之前必须保证相关单元的正确性。孤立的测试策略比较独立,覆盖率容易保证,并且可以并行进行,但工作量最大。一般采用混合方法比较好。
  • 45. 2.8 单元测试过程需求分 析阶段概要设计单元测试计划编码系统测试执行单元测试执行集成测试执行详细设计单元测试执行单元测试设计单元测试实现
  • 46. 2.8.1 测试计划阶段单元测试计划阶段:完成单元测试计划 计划阶段应当考虑整个单元测试过程的时间表,工作量,任务的划分情况,人员和资源的安排情况,需要的测试工具和测试方法,单元测试结束的标准及验收的标准等,同时还应当考虑可能存在的风险,以及针对这些风险的具体处理办法,并输出《单元测试计划》文档,作为整个单元测试过程的指导。
  • 47. 2.8.2 测试设计阶段单元测试设计阶段:完成单元测试方案 设计阶段需要具体考虑对哪些单元进行测试,被测单元之间的关系以及同其他模块之间单元的关系,具体测试的策略采用哪一种、如何进行单元测试用例的设计、如何进行单元测试代码设计、采用何种工具等,并输出《单元测试方案》文档,用来指导具体的单元测试操作。
  • 48. 2.8.3 测试实现阶段单元测试实现阶段:完成单元测试用例、单元测试规程、单元测试脚本及数据文件 实现阶段需要完成单元测试用例设计、脚本的编写,测试驱动模块的编写,测试桩模块的编写工作,输出《单元测试用例》文档、相关测试代码。
  • 49. 2.8.4 测试执行阶段单元测试执行阶段:执行单元测试用例,修改发现的问题并进行回归测试,提交单元测试报告 执行阶段的主要工作是搭建单元测试环境,执行测试脚本,记录测试结果,如果发现错误,开发人员需要负责错误的修改,同时进行回归测试,该阶段结束需要提交《单元测试报告》。 单元测试过程可根据实际情况进行适当裁减。
  • 50. 2.9 单元测试的原则对全新的代码或修改过的代码进行单元测试 单元测试根据单元测试计划和方案进行,排除测试的随意性 必须保证单元测试计划、单元测试方案、单元测试用例等经过评审 当测试用例的测试结果与预期结果不一致时,单元测试的执行人员需如实记录实际的测试结果 只有当测试计划中的结束标准达到时,单元测试才能结束 对被测试单元需达到的一定的代码覆盖率要求 代码行小于30的函数不需要做单元测试,通过代码走读保证函数质量 建议对代码行数大于200的函数重构,此类函数不适合做单元测试
  • 51. 课程内容第一章 详细设计及伪码简介 第二章 单元测试理论 第三章 逻辑覆盖率 第四章 单元测试用例设计 第五章 单元测试开展整体思路 第六章 单元测试环境搭建和单元测试执行
  • 52. 逻辑覆盖率白盒测试覆盖率中使用的最常见的就是逻辑覆盖率(Logical Coverage),也叫代码覆盖率(Code Coverage)或结构化覆盖率(Structural Coverage)。 我们经常接触到的逻辑覆盖包括: 语句覆盖 判定覆盖 条件覆盖 判定条件覆盖 路径覆盖等 下面我们来详细解释这些覆盖的概念。
  • 53. 3.1 语句覆盖 语句覆盖(Statement Coverage)的含意是,在测试时运行被测程序后,程序中被执行到的可执行语句的比率: 语句覆盖率 = (至少被执行一次的语句数量)/(可执行的语句总数) 例: … if (A>1 && B=0) {X=X/A} if (A=2 || X>1) {X=X+1} …
  • 54. 语句覆盖在测试时,首先设计若干个测试用例,然后运行被测程序,使程序中的每个可执 行语句至少执行一次 A = 2 B = 0 ………………CASE1 X = 3 A = 2 B = 1 ………………CASE2 X = 3 CASE1 能达到100%语句覆盖 CASE2 不能达到100%语句覆盖 思考:CASE1虽然达到100%覆盖,但测试是否彻底?
  • 55. 3.2 判定覆盖 分支覆盖(Branch Coverage)也叫判定覆盖(Decision Coverage),它的含 义是,在测试时运行被测程序后,程序中所有判断语句的取真分支和取假分支 被执行到的比率: 判定覆盖率= (判定结果被评价的次数)/(判定结果的总数)
  • 56. 判定覆盖在测试时,首先设计若干个测试用例,然后运行被测程序,使得程序中每个判断的取真分支和取假分支至少经历一次,即判断的真假值均曾被满足。 A = 2 B = 0 ………………CASE1:路径ace X = 3 A = 1 B = 0 ………………CASE2:路径abd X = 1 CASE1 CASE2能达到100%分支覆盖 思考:CASE1和CASE2虽然能够达到100%覆盖,但测试是否彻底?
  • 57. 3.3 条件覆盖条件覆盖(Condition Coverage)的含义是,在测试时运行被测程序后,所有判断语句中每个条件的可能取值(真值和假值)出现过的比率: 条件覆盖率=(条件操作数值至少被评价一次的数量)/ (条件操作数值的总数)
  • 58. 条件覆盖在测试时,首先设计若干个测试用例,然后运行被测程序,要使每个判断中每个 条件的可能取值至少满足一次 T4取真T3取真T2取真T1取真标记取值条件F4取假X>1F3取假A=2F2取假B=0F1取假A>1
  • 59. 条件覆盖T1,F2,F3,T4 a b e 3 1 2CASE3F1,T2,F3,F4 a b d 1 0 1CASE2T1,T2,T3,T4 a c e2 0 3CASE1覆盖条件所走路径A B X测试用例CASE1 CASE2 CASE3能达到100%条件覆盖
  • 60. 3.4 判定条件覆盖 分支条件覆盖(Branch Condition Coverage)也叫判定条件覆盖(Decision Condition Coverage),它的含义是,在测试时运行被测程序后,所有判断语句中每个条件的所有可能值(为真为假)和每个判断本身的判定结果(为真为假)出现的比率: 分支条件覆盖率= (条件操作数值或判定结果至少被评价一次的数量)/(条件操作数值总数+判定结果总数)
  • 61. 判定条件覆盖在测试时,首先设计若干个测试用例,然后运行被测程序,使得判断中每个条件的所有可能至少出现一次,并且每个判断本身的判定结果也至少出现一次 F3,T4A ≠2,X > 1⑦T3,F4 A =2,X ≤ 1⑥T3,T4A =2,X > 1⑤F1,F2 A ≤1,B ≠ 0 ④F1,T2 A ≤1,B = 0③T1,F2A > 1,B ≠ 0②T1,T2A > 1,B = 0①F3,F4 A ≠2,X ≤ 1⑧标记条件取值组合编号
  • 62. 判定条件覆盖abdabeabeace 所走 路径1 1 11 0 32 1 12 0 3 ABX F1,F2,F3,F4④ ⑧CASE4F1,T2,F3,T4③ ⑦ CASE3T1,F2,T3,F4② ⑥ CASE2T1,T2,T3,T4① ⑤CASE1覆盖 条件覆盖 组合测试 用例1、以上四个测试用例覆盖了100%条件、分支 思考:虽然以上用例达到了100%条件,分支覆盖,但测试是否彻底?
  • 63. 3.5 路径覆盖 路径覆盖(Path Coverage)的含义是,在测试时运行被测程序后,程序中所有可能的路径被执行过的比率: 路径覆盖率= (至少被执行到一次的路径数)/(总的路径数)
  • 64. 3.5 路径覆盖在测试时,首先设计若干个测试用例,然后运行被测程序,要求覆盖程序中所有 可能的路径 acdabeabdace 覆盖路径3 0 12 1 11 0 12 0 3ABXCASE4CASE3CASE2CASE1测试用例
  • 65. 3.5 路径覆盖路径能否全面覆盖在软件测试中是个重要问题,如果程序中的每一条路径都得到考验,才能说程序受到了全面检验 即使对于路径数很有限的程序已经作到了路径覆盖,仍然不能保证被测程序的正确性
  • 66. 路径覆盖的困难如图包含的不同执行路径数达5的20次方条,假定对每一条路径进行测试需要1毫秒,一年工作365*24小时,要想把所有路径测试完,需3170年测试中做到完全的路径覆盖是无法实现的,为解决这一难题只得把覆盖的路径数压缩到一定限度内
  • 67. 基本路径覆盖法基本路径覆盖法是在程序控制流图的基础上,通过分析控制结构的环路复杂性,导出基本可执行路径集合,设计测试用例的方法。 该方法把覆盖的路径数压缩到一定限度内,程序中的循环体最多只执行一次。 设计出的测试用例要保证在测试中,程序的每一个可执行语句至少要执行一次
  • 68. 程序控制流图
  • 69. 程序控制流图在选择或多分支结构中,分支的汇聚处应有一个汇聚结点 边和结点圈定的区域叫做区域,当对区域计数时,图形外的区域也应记为一个区域 如果判断中的条件表达式是由一个或多个逻辑运算符(OR,AND, NAND,NOR)连接的复合条件表达式,则需要改为一系列只有单条件的嵌套的判断。
  • 70. 基本路径覆盖方法
  • 71. 基本路径覆盖方法
  • 72. 程序环路复杂性程序的环路复杂性: 程序基本路径集中的独立路径数量,这是确保程序中每个可执行语句至少执行一次所必需的测试用例数目的上界 独立路径: 至少包含有一条在其它独立路径中从未有过的边的路径程序环路复杂性和区域数一致
  • 73. 基本路径覆盖法例如,在图示的控制流图中,一组独立的路径是 path1:1-11 path2:1-2-3-4-5-10-1-11 path3:1-2-3-6-8-9-10-1-11 path4:1-2-3-6-7-9-10-1–11 路径 path1,path2,path3,path4组成了控制流图的一个基本路径集
  • 74. 基本路径覆盖法步骤从详细设计导出流图 确定流图的环路复杂度 确定独立路径的基本集 导出测试用例,确保基本路径集中的每一条路径的执行 据判断结点给出的条件,选择适当的数据以保证某一条路径可以被测试到—用逻辑覆盖方法
  • 75. 循环路径测试简单循环 连锁循环 嵌套循环 非结构循环
  • 76. 四种循环路径
  • 77. 简单循环的路径选择零次循环:从循环入口到出口 一次循环:检查循环初始值 m次循环: 检查更多次循环,反映执行典型的循环的执行次数 最大次数循环、比最大次数多一次、少一次的循环 对于增量和减量不是1的循环,要特别注意
  • 78. 嵌套循环的路径选择对最内层循环做简单循环的全部测试。所有其它层的循环次数置为最小值 逐步外推,对其外面一层循环进行测试。测试时保持所有外层循环的循环次数取最小值,所有其它嵌套内层循环的循环次数取“典型”值 反复进行,直到所有各层循环测试完毕 对全部各层循环同时取最小循环次数,或者同时取最大循环次数
  • 79. 连锁循环路径选择如果各个循环互相独立,则可以用与简单循环相同的方法进行测试。 但如果几个循环不是互相独立的,则需要使用测试嵌套循环的办法来处理
  • 80. 非结构循环的路径选择这一类循环应该使用结构化程序设计方法重新设计,并重新设计测试用例
  • 81. 逻辑覆盖率统计实践练习1:采用程序插装思想进行覆盖率统计检测 练习2:采用BullsEyeCoverage工具对逻辑覆盖率检测 案例:IsCodeLine 操作系统: Windows 编译器: Visual c++ 6.0 覆盖率分析工具:Bullseyecoverage
  • 82. 课程内容第一章 详细设计及伪码简介 第二章 单元测试理论 第三章 逻辑覆盖率 第四章 单元测试用例设计 第五章 单元测试开展整体思路 第六章 单元测试环境搭建和单元测试执行
  • 83. 4.1 单元测试用例设计思路以黑盒用例设计方法为主,以白盒用例设计方法为辅 黑盒方法: 等价类 域测试法 正交试验法 错误猜测法 白盒方法: 通过分析逻辑覆盖率的覆盖情况,补充测试用例
  • 84. 等价类方法采用等价类划分,是将系统的输入域划分为若干部分,然后从每个部分选取少数代表性数据进行测试,这样可以避免穷举法产生的大量用例。 等价类是指某个输入域的子集合,在该子集合中,各个输入数据对于揭露软件中的错误都是等效的。并合理地假定:测试某等价类的代表值就等于对这一类其它值的测试。因此,可以把全部输入数据合理划分为若干等价类,在每一个等价类中取一个数据作为测试的输入条件,就可以用少量代表性的测试数据取得较好的测试结果。 等价类划分可有两种不同的情况,有效等价类和无效等价类: 1、有效等价类:是指对于系统的规格说明来说是合理的,有意义的输入数据构成的集合。利用有效等价类可检验程序是否实现了规格说明中所规定的功能和性能; 2、无效等价类:是指对于系统的规格说明来说是不合理或无意义的输入数据所构成的集合
  • 85. 步骤1:划分等价类1.在输入条件规定了取值范围或值的个数的情况下,则可以确立一个有效等价类和两个无效等价类。 2.在输入条件规定了输入值的集合或者规定了必须如何的条件的情况下,可确立一个有效等价类和一个无效等价类。 3.在输入条件是一个布尔量的情况下,可确定一个有效等价类和一个无效等价类. 4.在规定了输入数据的一组值假定n个,并且程序要对每一个输入值分别处理的情况下,可确立n个有效等价类和一个无效等价类 5.在规定了输入数据必须遵守的规则的情况下,可确立一个有效等价类符合规则和若干个无效等价类从不同角度违反规则. 6.在确知已划分的等价类中各元素在程序处理中的方式不同的情况下,则应再将该等价类进一步的划分为更小的等价类
  • 86. 在划分过程中,划分结果可以填写到下表:输入编号输入名有效等价类有效等价类编号无效等价类无效等价类编号
  • 87. 步骤2:确定测试用例从划分出的等价类中按以下三个原则设计测试用例: 1.为每一个等价类规定一个唯一的编号 2.设计一个新的测试用例,使其尽可能多地覆盖尚未被覆盖地有效等价类,重复这一步,直到所有的有效等价类都被覆盖为止 3.设计一个新的测试用例,使其仅覆盖一个尚未被覆盖的无效等价类,重复这一步,直到所有的无效等价类都被覆盖为止用例编号测试用例覆盖等价类编号
  • 88. 域测试法首先介绍一下边界值分析方法。边界值分析方法的理论基础,是假定大多数的错误是发生在各种输入条件的边界上,如果在边界附件的取值不会导致软件出错,那么其它的取值导致软件错误的可能性也很小。 域测试法是在对输入域进行等价类划分的基础上,对每个等价类区域进行边界值分析,在进行边界值分析时,引入了与域的概念相关的上点、离点与内点的概念。这种方法是由等价类划分以及边界值分析方法进一步发展而来,涵盖了等价类划分和边界值分析方法。 若输入域能够被划分为多个等价类,域测试法就在这些等价类的边界上进行测试数据的选择。对于一个等价类来说,它是一个域,域中的数据被分为了三类:上点、离点与上点。其中上点与离点就是域边界上的数据,内点是域内非边界上的数据。下面详细对这些概念进行说明。
  • 89. 基本定义上点:边界上的点,如果域的边界是封闭的,上点就在域范围内;如果域的边界是开放的,上点就在域范围外。 离点:就是离上点最近的一个点,如果域的边界是封闭的,离点就在域范围外,如果域的边界是开放的,离点就在域范围内。 内点:顾名思义,就是在域范围内的任意一个点(如果有特殊值,则取特殊值)。 关于上点、离点、内点,请参见下面图示:闭区间开区间半开半闭区间上点离点内点上点离点内点上点离点内点
  • 90. 步骤1:划分等价类(可选)1.在输入条件规定了取值范围或值的个数的情况下,则可以确立一个有效等价类和两个无效等价类。 2.在输入条件规定了输入值的集合或者规定了必须如何的条件的情况下,可确立一个有效等价类和一个无效等价类。 3.在输入条件是一个布尔量的情况下,可确定一个有效等价类和一个无效等价类. 4.在规定了输入数据的一组值假定n个,并且程序要对每一个输入值分别处理的情况下,可确立n个有效等价类和一个无效等价类 5.在规定了输入数据必须遵守的规则的情况下,可确立一个有效等价类符合规则和若干个无效等价类从不同角度违反规则. 6.在确知已划分的等价类中各元素在程序处理中的方式不同的情况下,则应再将该等价类进一步的划分为更小的等价类
  • 91. 输入编号输入名有效等价类编号有效等价类无效等价类编号无效等价类
  • 92. 步骤2:分析样点针对每个等价类区域分析其上点、离点、内点,结果填写到下表:等价类编号等价类名上点离点内点
  • 93. 步骤3:确定测试用例从划分出的等价类中按以下三个原则设计测试用例: 为每一个等价类内点、上点或离点规定一个唯一的编号 设计一个新的测试用例,使其尽可能多地覆盖尚未被覆盖地有效等价类的内点、上点或离点,重复这一步,直到所有的有效等价类点都被覆盖为止 设计一个新的测试用例,使其仅覆盖一个尚未被覆盖的无效等价类的内点、上点或离点,重复这一步,直到所有的无效等价类的内点、上点或离点都被覆盖为止用例编号测试用例覆盖等价类编号覆盖的样点
  • 94. 正交试验法概念: 正交试验设计法,是从大量的试验点中挑选出适量的、有代表性的点,应用依据迦罗瓦理论导出的“正交表”,合理的安排试验的一种科学的试验设计方法。 指标: 通常把判断试验结果优劣的标准叫做试验的指标 因子: 所有影响试验指标的条件 因子的状态: 而影响试验因子的,叫做因子的状态
  • 95. 正交试验法步骤提取功能说明,构造因子-状态表 找出合适的正交试验表 将因子和状态填写到表中,得到相应的测试组合因子1因子2…因子n状态1 状态2…状态m
  • 96. 正交试验表(1)
  • 97. 正交试验表(2)因子 状态1234 111112122231333421235223162312731328321393321
  • 98. 单元测试用例设计实践案例:Counter系统的IsCodeLine函数 详细设计文档参见: 《Counter V1.0 详细设计说明书(评审后).doc》 课堂练习: 详细设计分析 单元测试用例设计
  • 99. 课程内容第一章 详细设计及伪码简介 第二章 单元测试理论 第三章 逻辑覆盖率 第四章 单元测试用例设计 第五章 单元测试开展整体思路 第六章 单元测试环境搭建和单元测试执行
  • 100. 单元测试开展整体思路详细设计 详细设计撰写规则 详细设计评审 编码 编码规范 代码静态检查 代码走读(审查) 单元测试用例设计 黑盒测试用例设计方法 白盒用例设计方法 以黑盒为主,白盒为辅的测试用例设计思路 单元测试执行 借助工具搭建单元测试环境(CUnit,CppUnit,DUnit,Nunit,sqlUnit……) 撰写驱动函数和桩函数 构造测试数据 单元测试结果分析 根据测试覆盖率对单元测试效果评估
  • 101. 代码走读(审查)实践软件代码走读技巧 参见《软件审查技巧.doc》 代码审查九句真言 参见代码审查九句真言 代码审查发现的典型错误 《代码走读发现的典型错误.doc》
  • 102. Pclint静态检查实践PcLint简介及应用 参见文档《pc-lint使用.doc》 案例:SuperMarket
  • 103. 代码评审准备预审审查会议第三小时修改启动会 跟踪到关闭会议介绍复查并记录普遍异常复查并记录特定异常复查异常记录作出决议诸葛亮会延续会议两 小 时 以 内计划、分发材料可选:15分钟左右代码: 100L/h;文档: 5P/h 问题太多的话可取消评审代码:100L/h 文档: 5P/h为过程改进和缺陷预防建言献策讨论未决问题或解决方案对修改后部分再次评审
  • 104. 课程内容第一章 详细设计及伪码简介 第二章 单元测试理论 第三章 逻辑覆盖率 第四章 单元测试用例设计 第五章 单元测试开展整体思路 第六章 单元测试实践
  • 105. 单元测试环境搭建步骤单元测试环境搭建步骤: 编译得到单元测试环境所需的dll,lib 创建单元测试容器工程 添加单元测试框架代码 创建驱动文件 创建桩文件 添加被测试函数所在的文件 执行单元测试
  • 106. 单元测试实践一编写代码实现自动化测试框架: 案例:IsCodeLine函数 环境:VC 操作: 分析详细设计 编写测试用例 搭建测试环境,编写驱动或者桩 执行测试 统计覆盖率 完善测试 具体参见:6.1 手工创建自动化框架\Test
  • 107. 单元测试实践二借助CppUnit实现自动化测试框架: 案例:IsCodeLine函数 环境:VC 操作: 分析详细设计 编写测试用例 搭建测试环境,编写驱动或者桩 执行测试 统计覆盖率 完善测试 具体参见:6.2 CppUnit自动化测试框架
  • 108. 单元测试的讨论话题单元测试开展的困难 主管的态度 资源和进度的冲突 参与人员对单元测试的态度 单元测试的准备工作 单元测试与TDD的关系 什么是TDD?TDD就是把你的需求用测试给描述出来
  • 109. 单元测试工具的部分列表代码静态分析工具:Logiscope,McCabe QA,CodeTest 等 代码检查工具:PC-LINT,CodeChk,Logiscope 测试脚本工具:TCL,Python,Perl等 覆盖率检测工具:Logiscope, PureCoverage,TrueCoverage,McCabe Test ,CodeTest ,Clover,Bullseye coverage等 内存检测工具:Purify,BoundsCheck,CodeTest等 专为单元测试设计的工具:RTRT,Cantata,AdaTest、JUnit、CppUnit、 SqlUnit、Nunit等
  • 110. 单元测试资源推荐网站资源: http://www.51testing.com http://opensourcetesting.org http://www.sourceforge.net http://www.misra.org.uk
  • 111. 答疑