单元测试概念:
1什么是单元测试:对软件的基本组成单元所作的测试(面向过程(一组函数或一个函数)面向对象(类中得方法))
2语句:真正的处理语句才算是语句(判断框中的语句不算)。
判定:流程图中的菱形框;
判定数:流程图中菱形框的个数;
分支:判定的真或假;
分支数:判定数*2;
条件:判定中关系(比较)运算符;
条件数:判定中关系(比较)运算符的个数;
条件结果数:条件数*2(每个条件有真、假两个取值)。
3单元测试的目的:与LLD是否符合;与SRS是否符合;编程是否存在问题。
4关注重点包括:单元接口(类型,顺序,长度,参数个数);局部数据结构;独立路径;边界值;出错处理。
5单元测试环境包括:被测单元、驱动单元(把被测单元驱动起来,完成被测单元的调用)、桩单元(被测单元的调用的替代品,替代输入与输出),测试用例(测试数据)。
6驱动单元的四个职责1)接收侧四数据包含测试用例输入好预期输出
2)吧测试用例输入传送给要测试的单元
3)将被测单元的实际输出和预期输出进行比较,得到测试结果
4)将测试结果输出到指定位置
7辅助技术:评估,框架应用
8桩单元:通过一组输入和输出模拟被替代单元的行为
单元测试静态测试:
1.从详设的评审开始介入
2.详设评审、编码完成后,作代码的静态检查
(可以用专门的检查工具 如:PC_lint)
3.代码的交叉走读 (要制定标准,标准越清晰,任务被分配者越有目标,工作越细)
单元测试动态测试:
1写用例之前,先确定覆盖率标准
2写用例
3搭建测试环境
4执行
5测试报告
测试评价准则:
系统测试评价准则:需求覆盖率(=至少被用例覆盖一次的需求项数/测试需求分析列表中的需求项数)。
单元测试评价准则:逻辑覆盖率(=item至少被评估一次的次数/item总数)【这是一个总公式】。
逻辑覆盖率
逻辑覆盖率包括以下几种:语句覆盖率、分支覆盖率、条件覆盖率、分支条件覆盖率、路径覆盖率。【掌握计算公式、每种覆盖率的问题】
1语句覆盖率=语句至少被执行一次的次数/语句总数。
问题:有可能语句覆盖率为100%,有可能发现逻辑运算符的问题
2判定覆盖率=每个分支取值至少被执行一次的次数/分支总数(判定数*2)
问题:当分支覆盖率为100%时,可能不能发现关系运算符的缺陷
3条件覆盖率=每个条件取值至少被执行一次的次数/条件结果总数(条件数*2)
问题:条件覆盖率为100%时,有可能某个分支没有执行到,若该分支有缺陷,可能会遗漏。
4分支覆盖率=(每个分支至少被执行一次的次数+每个条件取值至少被执行一次的次数)/(分支总数+条件结果数)
问题:分支条件覆盖率为100%时,有可能漏路径,若该路径上有缺陷,可能遗漏
5路径覆盖率=每个路径至少被执行一次的次数/路径总数
问题:路径覆盖率为100%时,条件覆盖率可能不为100%
注:独立路径覆盖,若路径覆盖率100%则条件、语句、分支覆盖率均100%
路径一定要从始点到终点
可以用软件来计算路径覆盖率 如BullseyeCoverage
单元测试策略
孤立、自顶向下、自底向下。
⑴ 孤立测试
缺点:需要大量的桩和驱动
优点:改方法是最简单最容易操作的 ,可以达到高的结构覆盖率,该方法是纯粹的单元测试
方法:不考虑每个模块与其他模块之间的关系,为每个模块设计桩模块和驱动模块,每个模块进行独立的单元测试
【例1】
对象 | 驱动 | 桩 |
A | Driver-A | Stub-B |
B | Driver-B | Stub-C |
C | Driver-C | X |
【例2】
对象 | 驱动 | 桩 |
A | Driver-A | Stub-B,C |
B | Driver-B | Stub-D |
C | Driver-C | Stub-E |
D | Driver-D | X |
E | Driver-E | X |
⑵自顶向下的单元测试策略
方法:先对最顶层的单元进行测试,把顶层所调用的单元做成桩模块。其次,对第二层进行测试,使用上面已测试的单元做驱动模块。如此类推直到测试完所有模块。
优点:可以节省驱动函数的开发工作量,测试效率高。
缺点:随着被测单元一个一个被加入,测试过程将变得越来越复杂,并且开发和维护的成本将增加。
【例1】
对象 | 驱动 | 桩 |
A | Driver-A | Stub-B |
B | Driver-A | Stub-C |
C | Driver-A | Stub-D |
D | Driver-A | X |
a. 看被测函数下边有没有调用,如果被调用则打一个桩
b. 假如曾经被测过的函数的时候,这些函数下边是否有调用,而那些调用是否也被测过,如果没有被测过,也需要打桩
c. 只要驱动单元驱动的时候,所依赖的函数要run的时候,相关的调用都需要加入
⑶自底向上的单元测试方法
方法:先对模块调用层次图上最低层的模块进行单元测试,模拟调用该模块的模块做驱动模块。然后再对上面一层做单元测试,用下面已被测试过的模块做桩模块。以此类推,直到测试完所有的模块
优点:可以节省桩函数的开发工作量,测试效率较高。
缺点:不是纯粹的单元测试,底层函数的测试质量对上层函数的测试将产生很大的影响。
如果桩难写,采用自底向上;如果驱动难写,采用自顶向下。
打桩原则:1被测函数有没有调动,若有调动则打桩
2加入曾经被测过的函数,这些函数是否有调动,这些调动是否被测过,都没有则打桩
单元测试用例设计(基本路径覆盖法)★ (面试)
步骤:(所有的循环仅执行一次)
1)画流程图
2)根据流程图画出流图
3)根据流图找出独立路径
4)根据独立路径设计用例
结点:表示一个或者多个无分支的语句
边:表示处理流向
分支:判定真假的语句
区域:由结点和边构成的,域的个数等于独立路径的条数
注:路径一定要从始点到终点
复杂性计算 :V(G)=E-N+2
V ( G ) =P+1
E:边,
N:结点数量,
P:条件结点的数量,
V最好不要大于10
程序控制流图
1顺序结构
2if结构
3while循环结构
4until重复结构
5Case分支结构(swith结构)
单元测试执行
单元测试执行的过程:
单元计划---单元设计---单元测试用例脚本----单元测试执行
1.搭建环境 引入googletest
a) 将googletest的include和lib两个文件夹复制到VS的项目文件夹下
b) 在Visual Studio中选择项目,属性,将include和lib导入
c) 引入googletest头文件
#include "gtest/gtest.h"
d) 在main函数中初始化GTEST
testing::InitGoogleTest(&argc,argv); 初始化google test
RUN_ALL_TESTS(); 运行当前工程所有tes
2.加入被测代码
将被测代码头文件引入
3.新建测试代码
新建一个文件,编写测试代码
调用googletest宏
TEST(测试套名字,测试用例名字)
{
……..
}
用断言函数(ASSERT、EXPECT)来判断预期结果与实际结果
ASSERT:致命断言,若前一个执行失败,则不继续执行
EXPECT:一般断言,前一个执行失败,继续执行
系统测试中发现不了在单元测试中比较容易发现的问题,如内存泄露、算法缺陷
补充:
C语言中,用malloc申请内存,free释放内存
C++中,用new申请内存,delete释放内存
TDD(测试驱动开发)
内存中的堆、栈
编译器自动分配的内存是在栈中,栈会自动维护、清理
手工分配的内存是在堆中,堆不会自动清理,需要手工释放,容易忽略造成内存泄露
单元测试框架
1. 框架是一组为重用而设计的方法,单元测试框架包括junit\cppunit\phpunit\c#unit\pyunit
2. pyunit是 d:\py26\unittest.py
3. 单元测试框架概念
测试固件(test fixture)
一组测试函数执行前或执行后要执行的语句。
例如,void insertDB(chr sql[30])
{
//执行insert语句
}
若现在测试该函数,实际上每次测试之前都要建立数据库连接,测试完成之后都要断开断开数据库连接。
建立连接和断开连接称之为测试固件。测试固件在框架如何体现:
setUP(...) ---表示测试函数执行之前要执行的语句,比如建立连接
teardown(...) ---表示测试函数执行之后要执行的语句,比如断开连接
有没有测试固件,取决于被测函数在测试执行需要否
ü 测试用例
主要以测试函数为主,什么是测试函数?就是对被测单元完成测试的函数,类似于原始的驱动单元。
在框架中一般来说要继承单元测试框架的TestCase。
ü 测试套件
装配测试用例的车间。
在框架中一般来说使用TestSuite的addTest方法来进行装配,装配的就是测试用例,实际上装配的用例中的测试函数。
ü 测试运行器
加载测试套件,对套件中装配的测试函数的运行。
在框架中一般来说使用TestRunner中的run方法来进行运行TestSuite。
4. pyunit框架中有:
unittest.py
测试固件 -----》TestCase类中有setUp和tearDown有这两个方法
测试用例 -----》TestCase类,在该类中写测试函数的方法 ????
测试套件 -----》通过TestSuite类中的addTest方法将测试用例中的测试函数加载
测试运行器-----》通过TextTestRunner中的run方法将测试套件运行起来。
5. 实例:
见例子
6. 使用pyunit单元测试步骤:
1)import unittest
2)继承unittest.TestCase构建自己的测试类
3)在自己构建的测试类中,若对于测试函数有测试固件,则在setUp和tearDown中进行代码编写。否则跳到1)