回顾测试理论基础
单元测试基础知识
什么是单元测试
单元测试流程、测试计划
测试策略设计、实现
单元测试 - 执行
HTML 报告生成
1 软件测试分类
目标
回顾测试理论知识-测试分类
1. 测 试分类 代码可见度上-划分分类:
1. 黑盒测试
2. 灰盒测试
3. 白盒测试
1.1 黑盒测试
作用:是针对整个产品系统进行的测试,验证系统是否满足需求规格的定义,及软件产品的正确性和性能等
是否满
足其需求规格的要求。
1.2 灰盒测试
作用:是介于白盒测试与黑盒测试之间的一种测试,灰盒测试多用于集成测试阶段,不仅关注输出、输入的
正确性
,同时也关注程序内部的情况。
1.3 白盒测试
作用:是通过程序的源代码进行测试而不使用用户界面。这种类型的测试需要从代码内部在算法,路径,条件等等中的缺点或者错误,进而加以修正。
2单元测试基础
1.1 案例
1. 组装一台电脑,选购各种配件 (CPU 、主板、内存、硬盘、电源 ) 等,组装完成后,电脑运行启动无反应。
2. 编写一个程序,编写完成程序运行不起来 ( 程序由 N 个不同功能函数和语句组成 )
1.2 问题
1. 无法确定各配件哪个有问题。
2. 无法确定那个函数或语句出现问题。
2. 什么是单元测试?
概念:单元测试是针对程序的最小单元来进行正确性检验的过程。
单元:一个单元可能是单个程序、类、对象、方法(函数)等。
2.1 优点:
1. 减少 BUG
2. 快速定位 BUG
3. 提高代码质量
4. 减少调试时间
2.2 缺点:
1. 周期时间长
4 2. 耗费资源
3. 能力要求高
3. 单元测试 - 总结
1. 概念
2. 优点
3. 缺点
5
如何进行单元测试
目标
1. 了解单元测试流程
如何进行单元测试?
单元测试流程
1. 单元测试 - 计划
1) 确定要测试代码范围
2) 评估标准 ( 确定被测代码的覆盖率 )
2. 测试策略 - 设计
1) 拿到开发代码进行调整 ( 可独立执行 )
3. 测试策略 - 实现
1) 根据调整好的代码 - 画流程图
2) 根据流程图画流图 - 确定复杂度、路径
3) 根据复杂度和路径确定测试用例 ( 测试数据 )
4. 单元测试 - 执行
1) 使用测试框架 (UnitTest) 编写单元测试用例
2) 测试用例 ( 代码 ) 和测试数据分离
3) 生成测试报告
1. 单元测试-计划
概念:对要测试代码的确定以及这些被测代码的评估标准、优先级等说明
1. 确定单元测试范围 ( 那些代码要做单元测试 )
2. 评估标准 -( 被测代码的逻辑覆盖率 )
1.1 如何确定单元测试代码范围?
依据:二八原则 (20% 的代码中隐藏了 80% 的缺陷 )
如何确定20%代码?
1. 频率:使用频率高的代码段;
2. 复用性: ( 是否已被复用 )
1). 全新
2). 部分复用
3). 全部复用
3. 开发人员:
1). 技术
2). 业务
4. 复杂度:业务逻辑复杂度 ( 一般认为圈复杂度在 7 级以上代码包括在 20% 的代码中 )
如何确定圈复杂度等级?
圈复杂度:在学习测试策略实现时讲解
附录-测试范围汇总格式
1.2 评估标准
如何确定逻辑覆盖率?
1. 语句覆盖率
2. 分支覆盖率
3. 条件覆盖率
4. 路径覆盖率
5. 分支- 条件覆盖率
练习 1
提示用户输入一个数,如果该数大于 0 则加 1 ,否则减 1 ;
代码:
#提示用户输入一个数,如果该数大于0则加一,如果小于0则减一
num=int(input("请输入一个数:"))
if num>0:
num=num+1
elif num<=0:
num=num-1
print("num的值为:"+str(num))
流程图 :
流程图
说明:使用统一规定标准图形,描述程序运行的具体步骤
目的:
1) 确定覆盖率
2) 根据流程图画流图 (流图--> 测试策略实现时学习)
什么是语句覆盖率?
语句:非分支非判断的代码
练习1 测试数据
1. 输入 3=2/3
2. 输入 0=2/3
3. 输入 -2=2/3
语句覆盖率-总结
1. 语句
2. 计算方式:被覆盖语句 / 总语句
分支覆盖率
分支:判断语句的分支; 如 :if 判断有两个分支
练习1 分支覆盖率测试数据
本题有两个分支
1. 输入 3=1/2
2. 输入 0=1/2
3. 输入 -2=1/2
分支覆盖率-总结
1. 分支
1. 计算方式:覆盖分支 / 总分支数
条件覆盖率
条件:结果为 true 或 false
练习 2
输入用户名和密码,如果用户名为:admin, 密码为: 123456 ,输出验证成功;否则输出验证失败;
代码:
#输入用户名和密码,如果用户名为:admin,密码为:123456,输出验证成功;否则输出验证失败;
username=input("请输入用户名:")
password=input("请输入密码:")
#注意input输入的是字符串类型
if username=="admin" and password=="123456":
print("验证成功")
else:
print("验证失败")
流程图:
练习 2 分析
1. 条件个数
练习 2 测试数据
1. 用户名: admin 密码: 123456
2. 用户名: zhangsan 密码: 123456
3. 用户名: admin 密码: 456789
练习 3
输入两个数 a,b ,如果 a>5 and b<5 执行语句 3 ;否则 elif a==5 or b<10 执行语句 4 ;否则执行语句 5
代码:
流程图:
练习 3 分析
1. 条件个数
练习 3 测试数据
1. a=3,b=6
2. a=6,b=3
3. a=6,b=6
练习 4
9 输入用户名和密码:
如果 username= "admin" and password="123456" 执行语句 3; 否则执行语句 4
如果 username= "zhangsan" and password="654321" 执行语句 5 ;否则执行语句 6
练习 4 分析
1. 条件个数
练习 4 测试数据
1. 用户名: admin 密码: 123456
2. 用户名: zhang 密码: 123456
路径覆盖率
路径:从开始到结束的过程
分析练习 2 路径
1. 路径个数
分析练习 3 路径
1. 路径个数
练习 3 路径覆盖数据
1. a=6,b=3
路径覆盖率 - 总结
1. 路径概念
2. 计算方式:覆盖路径 / 全部路径
3. 路径和分支区别
分支 - 条件覆盖率
10 说明:分支和条件的组合
练习 2 测数据 分支 - 条件覆盖率
1. 用户名: admin 密码: 123456
分析练习 2 分支 - 条件覆盖率
1. 分支个数
2. 条件个数
3. 分支条件个数
练习 3 测试数据 分支 - 条件覆盖率
1. a=6,b=6
练习 3 分析 分支 - 条件覆盖率
1. 分支个数
2. 条件个数
3. 分支条件个数
练习 4 汇总
1. 语句覆盖率
2. 分支覆盖率
3. 条件覆盖率
4. 路径覆盖率
5. 分支条件覆盖率
提示:
练习 4 :路径有 4 条,但有一条永远不可成立,所以路径为 3 条;
练习 4 测试数据
1. 用户名: admin 密码: 123456
11 1.3 测试策略设计、实现
目标
1. 了解单元测试 - 测试策略
1. 什么是单元测试策略?
概念:针对单元测试选择测试的一种方式
2. 单元测试策略 - 方式
1. 自上向下
2. 自下向上
3. 孤立策略
2.1 策略 自上向下
方式:从最上层函数往下开始逐层测试
案例 1
两个函数 ( 求和、求减 ) ,求和函数内调用求减函数;
案例 1 分析
1. 编写求和函数
2. 编写求减函数
3. 在求和函数内调用求减函数
4. 调用求和函数
案例 1 问题
12 1. 我们只想测试求和函数不测求减函数该如何做?
打桩
概念:打桩就是模拟编写一个我们需要引用的函数
提示:一般我们只模拟写个函数名,直接返回相应的结果即可
示例:
def fun_1(self):
return true
自上向下 - 总结:
1. 方式
2. 打桩 ( 模拟调用的函数 )
3. 缺点 ( 成本高 )
2.2 策略 自下向上
方式:从最底层函数往上开始逐层测试
自下向上 - 总结:
1. 方式
2. 缺点 ( 周期长 )
2.3 策略 孤立策略
方式:选择需要进行测试的函数进行测试
孤立策略 - 总结:
1. 方式
2. 【推荐使用】
3. 优点:选择重要的代码进行测试
3. 测试策略实现
13 3.1 什么是测试策略实现?
概念:把我们选定的代码,转向流程图、流图及用例的过程
3.2 测试策略实现如何操作?
1. 将测试代码转换成流程图
2. 根据流程图转换为流图
案例 2
函数 (test001) 判断参数 a, 如果 a>0 ,输出语句 1 ;
案例 2 流程图
什么是流图?
概念:表达程序业务逻辑的复杂度一种示意图
构成:
1) 圈:判断条件、语句块 ( 一条或多条语句 ) 两者都圈
2) 线:箭头指向的线,用来连接圈和圈的指向
流图的目的?
1) 确定单元的复杂度级别
2) 确定测试用例
备注 : 路径的个数为复杂度的级别 ( 一条路径为 1 个复杂度级别 )
14 案例 2 流图
测试用例 模板
案例 2 总结
1. 路径: 2 (1-2-3,1-3)
2. 复杂度: 2
3. 用例个数: 2
案例 3
函数 (test002) 判断参数 a,b; 如果 a>0 and b<10 输出语句 1 ;否则输出语句 2 ;
代码 :
流程图 :
流图:
案例 3 总结
1. 复杂度
2. 路径明细
思考?
15 如果把案例 3 的条件 a>0 and b<10 换成 条件 1 or 条件 2 那么复杂度是多少?
案例 4
输入两个数,如果两个数同时为大于 0 小于 10 ,那么就进行求和运算;否则判断两个数是否同时大于等于 10 小
于 20 那么
就进行求减运算,否则输入错误;
代码:
流程图:
流图:
案例 4 总结
1. 路径数:
2. 路径明细:
3. 复杂度:
案例 5 while 循环
使用 while 循环求 1-10 相加之和;
代码:
流程图:
流图:
案例 5 总结
1. 路径数:
2. 路径明细:
3. 复杂度:
案例 6
提示输入三角形三条边 , 进行判断三角形类型 ( 等边、等腰、普通 ) 并进行提示,否则提示不是三角形;
提示:
三角形:两边之和大于第三边;
16 等腰三角形:两条边相等
等边三角形:三条边相等
案例 6 步骤分析
1. 代码:
1) 自定义函数
2) 判断是否为三角形
3) 判断是否为等边三角形
4) 非等边三角形继续判断是否为等腰三角形
5) 如果不是等腰三角形,输出普通三角形
2. 流程图
3. 流图
4. 用例
总结
1. 单元测试计划
2. 测试策略设计
3. 测试策略实现
17 1.4 单元测试 - 执行
目标
1. 回顾 UnitTest 框架使用
2. 基于 UnitTest 测试三角形案例
3. 在 UnitTest 框架中使用数据分离
什么是单元测试执行?
概念:通过单元测试框架对要进行测试代码的实践过程
1. 练习 1
1. 通过 Python 语言编写一个运算的类 (Calc) ,类中包含两个函数:
1) add(self,a,b) 返回 a+b 之和
2) sub(self,a,c) 返回 a-c 之差
提示:通过练习 1 ,我们先简单复习下 UnitTest 框架的使用
1.1 练习 1 步骤分析
1. 新建 Calc 类
2. 在 Calc 类中新建 add 函数
3. 在 Calc 类中新建 sub 函数
4. 调用对象中方法
1.2 练习 1 单元测试
1. 导包 UnitTest 、 Calc 类
2. 新建单元测试类 Test01( 集成 unitTest.TestCase)
3. 新建 testAdd() 函数
1). 导入 Calc 类中的 add 函数
18 2). 添加断言
4. 新建 testSub() 函数
1). 导入 Calc 类中的 sub 函数
2). 添加断言
5. 执行测试
1). unittest.main()
1.3 总结
1. 导包
2. setUp 函数作用
3. tearDown 函数作用
4. 断言
5. 运行测试函数
1.4 问题
1. 练习 1 我们数据直接写入代码中,如果数量庞大,我们需要频繁改动数据或复制冗余代码进行实现数据测
试目的。
2. 做不到数据分离 ( 代码和数据分开 ) ,不便维护;
参数化
概念:根据需求动态获取数据并进行赋值的过程
2. 参数化 - 方式
1. XML 格式( 1 )
3. CSV 格式( 2 )
2. JSON 串 ( 3 )
4. TXT 文本( 4 )
提示:括号内为推荐使用优先级,从小到大 ;1-2 为扩展了解, 3-4 建议重点了解
3. XML 是什么?
概念: XML 是一种标记语句,很类似 HTML 标记语言;后缀 .xml
19 3.1 XML 与 HTML 区别?
XML 是传输和存储数据,焦点在数据; HTML 是显示数据,焦点在外观;
3.2 XML 格式是什么?
面授 ">
单元测试
2008
1. 必须有 XML 声明语句:
2. 必须要有一个根元素,如:
3. 标签大小写敏感
4. 属性值用双引号
5. 标签成对
6. 元素正确嵌套
7. 标签名可随意命名 , 但有以下限制
1) 不能以数字或者标点符号开始参
2 )不能以字符 “xml” (或者 XML 、 Xml )开始
3) 名称不能包含空格
3.3 需求
对三角形案例单元测试使用 XML 格式进行参数化
3.4 操作步骤
1. 编写 XML 数据文件
2. 编写读取 XML 模块函数
3. 单元测试模块中引用 XML 读取函数
4. 执行
3.5 重点分析
1. 导入 XML 包 from xml.dom import minidom
2. 加载解析 dom=minidom.parse(filename)
20 3. 获取对象 root=dom.documentElement
4. 获取子元素 aas=root.getElementsByTagName(one)[0]
5. 获取子元素值 aas.getElementsByTagName(two)[0].firstChild.data
3.6 XML- 总结
1. 读取方式
2. 什么是 XML
3. XML 与 HTML 区别
4. XML 编写格式
5. 缺点:不适合大量参数化数据时使用
4. CSV 格式
概念: CSV 是一种以逗号做分割的表格格式 ; 后缀 .csv
4.1 使用 CSV 实现三角形案例参数化 - 操作步骤
1. 创建 CSV 文件
2. 编写 CSV 读取模块函数
3. 单元测试 - 引用 CSV 读取函数
4. 执行
4.2 重点分析
1. 导包 import csv
2. 打开 csv 文件
with open("../Data/sjx.csv","r",encoding="utf-8") as f:
lines=csv.reader(f)
4.3 CSV- 总结
1. 读取方式
2. 生成 CSV 文件方式
3. 编码
21 5. 什么是 JSON ?
概念:一种轻量级数据交换格式;后缀名 .json
提示:
接口测试一般使用 JSON 为接口传递数据规范格式,所以我们有必要对如何获取 JSON 数据做个了解;
5.1 JSON 格式
格式: {"name":" 张三 ","age":28}
提示:由键值对组成 , 健名和值之间使用分号 (:) 分割,多个键值对之间使用逗号 (,) 分割
5.2 使用 JSON 实现三角形案例参数化 - 操作步骤
1. 编写 JSON 文件
2. 编写 JSON 读取模块函数
3. 单元测试 - 引用 JSON 读取函数
4. 执行
5.3 难点分析
1. 导入 JSON 包( import JSON )
2. 打开 JSON 文件并解析
with open('../DataXML/sjx.json','r',encoding='utf-8') as f:
file=json.load(f)
5.4 JSON- 总结
1. JSON 概念
2. JSON 格式
3. 如何导入 JSON 包
4. 如何加载 JSON
6. TXT 文本
概念:一种纯文本格式 ; 后缀名 .txt
6.1 TXT 文本优点:
22 1. 编写测试数据方便
2. 使用模块函数读取时便捷
6.2 使用 TXT 实现三角形案例参数化 - 操作步骤
1. 创建 txt 文本并写入测试数据
2. 编写读取 txt 模块函数
3. 单元测试 - 引用 JSON 读取函数
4. 执行
6.3 难点分析
1. 如何读取 txt 文本?
with open(r'../DataXML/ 三角形 .txt','r',encoding='utf-8') as f:
2. 如何去除行尾 /n 换行符?
line.strip()
6.4 TXT 总结
1. TXT 文本读取
2. 去除行尾回车符 (/n)
7. 参数化 - 总结
1. XML
2. CSV
3. JSON
4. TXT
23 HTML 报告生成
目标
对单元测试生成 HTML 报告进行简单回顾
1. 如何生成 HTML 报告?
1.1. 导入 HTML 报告模板 模板
HTMLTestRunner.py
1.2 编写生成 HTML 模块
# 导入 unittest 包
import unittest
# 导入 HTMLTestRunner 模板包
from UnitTest.Day02.ReadData.HTMLTestRunner import HTMLTestRunner
# 导入时间包
import time
# 定义测试模块路径
dirpath='.'
disconver=unittest.defaultTestLoader.discover(dirpath,pattern='test*.py')
if __name__=='__main__':
# 存放报告的文件夹
report_dir='../TestReport'
# 报告名称含时间,时间格式
now=time.strftime("%Y-%m-%d %H_%M_%S")
# 报告完整路径 + 名称
report_name=report_dir+'/'+now+'result.html'
# 打开报告写入结果
with open(report_name,'wb')as f:
runner=HTMLTestRunner(stream=f,title="UnitTest Report-SJX",description='Tes
t Case Report')
runner.run(disconver)
1.3 生成报告示例图
24 25