当前课程知识点:软件工程 > 第3章 单元测试 > 3.4 单元测试工具 > 单元测试工具
大家好
今天我们来介绍一下
Python的单元测试工具和案例
Python的测试框架unittest
是受到unit启发产生的
并且内置在Python中
无需我们安装
unittest框架
主要有以下几个部分组成
TestCase类
它是一个最小的测试单元
我们通过集成这个类
来编写测试用例
一个测试用例
包含若干个以Test开头的
相互独立的测试方法
也可以使用setUp和tearDown
来为每一个测试创建销毁
TestSuite类
它是测试套件类
包含若干个TestCase
用于对测试进行分组管理
TestLoader和TestResult类
这两类在编写测试中
我们通常不会直接用到
一些基于Unittest工具
可能会用到
main方法
用于启动当前文件中的测试
测试中
我们需要对程序的结果进行判断
来验证结果是否符合我们的预期
及属性断言
例如assert关键字
就是断言的一种
不过assert关键字的功能比较弱
所以TestCase
它提供了多种强大的断言方法
大家可以在下面的链接中去查阅
这些断言方法
可以在断言的同时
加上一个message参数
使断言的意义更加明确
方便我们的用户
unittest主要步骤如下
首先我们需要import unittest包
然后我们继承unittest包中的
TestCase类创建测试用例类
接下来在其中定义setUp
和tearDown方法
在每个测试用例前后
做一些辅助处理
之后就可以
开始正式定义测试用例了
所有的测试用例
都是以test开头方法
这里需要注意
单元测试和集成测试不一样
它的测试用例
应该只测试一个方面的内容
并且测试目的和测试内容
都应该明确
主要是根据assertEqoal等断言
来判断执行结果是否预期
启动测试以后
我们调用unittest.main启动测试
运行测试
如果测试未通过
会输出错误提示
如果测试全部通过
则显示ok
添加-v参数
可以显示更多详细信息
Python3.3开始
内置了Mock框架
可以方便我们在测试中
用Mock对象
替换以下的真实的对象
控制对象的行为
并且记录一代对象
是如何被调用的
它主要有以下几部分组成
Mock类
用来创建Mock对象
当访问Mock对象的属性时
Mock对象会自动创建属性
并且为该属性也创建一个Mock
MagicMock类
是Mock类的一个子类
区别是它额外定义了操作符
比如大小比较 长度等等
patch装饰器
可以把patch装饰器
作用在测试方法上
用来限定当前测试中
使用mock来替换真实对象
有些用with关键字
创建一个作用域
mock对象提供了一些断言方法
用来我们断言对mock项的调用
大家可以在文档中
查看具体的断言方法的说明
另外我们通常需要从
依赖对象的方法上得到返回值
mock对象也提供了一些途径
来对返回值进行控制
Python单元测试覆盖工具
coverage.py
可以通过pip来安装
它可以用来记录程序中
每行语句的执行情况
即可以处理覆盖率
覆盖率工具在PyCharm上中
可以很方便地使用
主要在运行测试的时候
选择和覆盖率一起来运行
就可以在PyCharm项目中
看到覆盖率的结果
包括语句的覆盖情况
红色表示没有执行
绿色表示执行
还可以看到语句覆盖率的统计
下面我们通过一个案例
来学习一下Python单元测试
我们首先来分析一下生命游戏
生命游戏它包含四个文件
分别是主程序 输出程序
定时器 还有生命游戏
我们通过pylint
可以得到以下的依赖关系图
我们可以看到game_timer
还有game_map这两个是
最底层的操作
在这两个模块当中呢
game_map又是最核心的模块
它包含了生命游戏的主要逻辑
它有如下的属性方法
rows和cols属性
表示地图的行和列的数量
reset方法
用一个概率
来设置地图的每个格子的状态
get/set
获得设置某一个格子的状态
get_neighbor_count
获得某一个格子的邻居数量
get_neighbor_count_map
获得每一个格子的邻居数量
set_map设置整个地图
print_map打印地图
下面我们首先来创建测试
我们打开game_map.py
然后在菜单上选择NavigateTest
这时会在game_map类上
弹出一个小菜单
我们选择Create New Test
这时会弹出创建测试的对话框
我们在对话框中
选择了要测试的方法
这里把所有方法都选中
然后输入测试文件
测试类的名称
最后点击ok
这样我们就完成了测试类的创建
在上面它会自动打开测试文件
我们会看到
Test game_map这个类
它确实继承了testCase类
创建测试之后
我们首先来看一下如何运行测试
我们在Test game_map类的
类名上点击右键
选择运行
test game_map中的单元测试
然后我们就能够在PyCharm项目中
看到测试结果
这里一共有9个测试
之外的测试用户
这个感叹号在左边的窗口中
来显示
我们可以看到一共失败了9个
这是因为新创建的测试方法
它默认调用了
TestCase的私有方法
而这个方法
会无条件地使单元测试失败
下面我们来开始正式编写测试
我们首先来创建fixture
回顾一下setUp方法
它可以用来每个测试
都需要的公共对象
tearDown方法
用来销毁公共对象
比如数据库
断开连接
关闭文件等等
这里我们只需要setUp方法
在其中创建一个
game_map待测对象
简单起见
这里创建一个
四行三列的game_map
然后我们对于rows和cols
进行测试分析出
通过asserEqual
来判断行为4 列为3
然后我们重新运行测试
可以看到这两个测试通过了
接下来我们来看一下
get和set方法
这两个方法有密切联系
我们把它合并到一个测试当中
这里首先我们断言默认的情况下
每个格子的值应该都是0
然后我们给0 0格子设置为1
断言get应该返回
我们通过set设置的值
接下来我们运行测试 通过
然后是rest方法
这个方法它依赖于概率
所以需要我们进行mock
我们首先看一下
这个方法自己的代码
我们可以发现
它对于每一个行
每一列的一个格子
它都会生成一个随机数
然后判断
是否小于预先给定一个概率值
所以小于的话
就把这个格子设成1
否则设为0
我们通过Patch方式器
在TestReset方法中
把random模块的random方法
换为Mock对象
这个Mock对象
我们让它的行为是
返回一个0.3 0.6
0.9的循环序列
然后我们调用Reset方法
接下来我们断言第一列全部为1
后两列全部为0
然后测试 通过
接下来是
get_neighbor_count方法
从列表中我们看到
它需要访问cell属性
这里我们可以对它进行设置
我们首先将cell全部设为1
这样所有格子的邻居数量
都应该是8
我们发现测试失败了
对于第一个格子
它的邻居数量返回到4
而不是8
我们检查一下get_neighbor_count方法
这里由于cell全为1
所以最后的结果
只和循环次数有关
这样的话循环次数
只跟DIRECTIONS有关
我们检查一下directions
发现这里只写了四个相邻的方向
忽略了角的方向
这就是错了
我们把directions
修改为正确的值
然后重新运行测试 测试通过
get_neighbor_count_map
它依赖get_neighbor_count方法
测试的时候
我们要对依赖方法进行mock
保持测试的独立性
这样
get_neighbor_count_map的
正确性就不依赖于
get_neighbor_count的正确性了
同时我们之后看概率的时候
也不会相互之间有干扰
set_map它本身比较简单
所以我们这主要测试一下
它对于参数的检查是否完备
这里我们主要使用了
assertRaises来判断一个方法
是否抛出了异常
我们看测试通过
我们看到Print_map调用了print
所以我们还是可以通过为print
进行mock来测试
但是这样写本身是不好的
作为一个底层库
这里就要访问一个字符串
我们先设定cells
然后对Print函数进行mock
print函数builtins包底下
然后我们调用了
assert_has_calls断言
这个断言是断言这个方法
是对参数进行了调用
这里的call是mock包里的一个
代表函数调用的mock
它的参数表示调用参数
然后我们运行测试 发现通过
到此我们所有的测试都通过了
在所有的测试通过后
我们就可以进行测试覆盖分析了
我们来运行测试覆盖分析
然后可以看到
84%的行都被覆盖了
查看代码我们发现
没有覆盖的行
全都是这样的类型检查
事实上这些检查
可以通过注解之后
完全通过静态分析工具来检查
所以我们这些不继续测试了
通过大家有一个误区
就是追求百分之百的覆盖率
但是百分之百的覆盖率
其实并不能说明非常的正确
所以也不要得到
覆盖率等于正确率
这样的错误结论
除了以上介绍的工具外
Python
还有很多其他的测试单元工具
其他的测试框架
比如说Pythonnose
pytest等等
这里还有一个链接
来比较其他的Mock框架
最后这里有一个
Python测试工具大全的链接
里面几乎包括全部
Python值的测试框架
Mock框架 Web测试工具
GUI测试工具等等
有兴趣的话可以访问这个网站
来自学
以上便是今天的全部内容
感谢大家观看
-1.1 软件无处不在
--讲课视频
-1.2 软件的本质特性
--讲授视频
-1.3 软件工程的产生与发展
--讲授视频
-1.4 软件工程的基本概念
--讲授视频
-1.5 软件质量实现
--讲授视频
-1.6 业界人士谈软件工程
-测验题--作业
-讨论题
--讨论题
-作业题
--第一张 作业题
-2.1 编程过程与规范
--讲课视频
-2.2 良好的编程实践
--讲课视频
-2.3 Python集成开发环境
--讲课视频
-2.4 代码静态检查
--讲课视频
-2.5 代码性能分析
--讲课视频
-2.6 结对编程实践
--讲课视频
-2.7 刘贺谈软件工程
--讲课视频
--讨论
-测验题--作业
-作业题
--第二章 作业题
-3.1 单元测试概述
--讲课视频
-3.2 黑盒测试方法
--黑盒测试方法
-3.3 白盒测试方法
--基本概念
--代码覆盖标准
--基本路径测试
-3.4 单元测试工具
--单元测试工具
--html
-测验题--作业
-作业题
--第三章 作业题
--作业题附件
-4.1 软件过程
--讲课视频
-4.2 软件过程模型
--讲课视频
-4.3 敏捷开发过程
--讲课视频
-4.4 微软公司开发过程
--邹欣经理自我介绍
--微软开发过程之一
--微软开发过程之二
-测验题--作业
-5.1 团队组织与管理
--讲课视频
-5.2 项目沟通管理
--讲课视频
-5.3 软件项目计划
--讲课视频
-5.4 软件项目估算
--讲课视频
-测验题--作业
-讨论题
--讨论
-6.1 敏捷开发之Scrum
-- 敏捷开发之Scrum
--html
-6.2 用户故事与估算
--讲课视频
-6.3 团队协作工具Tower
-6.4 配置管理
--讲课视频
-6.5 配置管理工具Git
--讲课视频
-测验题--作业
-作业题--作业
-7.1 需求工程师
--讲课视频
-7.2 需求定义
--讲课视频
-7.3 需求的类型
--讲课视频
--讲课视频(2)
-7.4 需求工程过程
--讲课视频
-7.5 需求的主要来源
--讲课视频
-7.6 需求获取技术
--讲课视频
--讲课视频二
--讲课视频三
-7.7 撰写需求文档
--讲课视频
-测验题--作业
-讨论题
--讨论
-8.1 用例建模概念
--讲课视频
-8.2 用例建模过程
--讲课视频
-8.3 用例建模精讲
--讲课视频
-8.4 建模工具介绍
--讲课视频
-8.5 微信抢票应用案例
--讲课视频
-测验题--作业
-讨论题
--讨论
-9.1 面向对象分析
--讲课视频
-9.2 CRC卡片分拣法
--讲课视频-1
--讲课视频-2
-9.3 面向对象设计
--讲课视频-1
--讲课视频-2
-9.4 类图建模
--讲课视频-1
--讲课视频-2
-第9章 面向对象分析与设计--测验题
-讨论题
--讨论
-10.1 顺序图概念
--讲课视频
-10.2 顺序图建模
--讲课视频
-10.3 顺序图风格
--讲义视频
-10.4 状态建模
--讲课视频
-10.5 状态图
--讲课视频
-10.6 状态图精讲
--讲义视频
-测验题--作业
-讨论题
--讨论
-11.1 软件体系结构概念
--讲授视频
-11.2 软件设计原则
--讲授视频
-11.3 软件体系结构风格(一)
--讲授视频
-11.4 软件体系结构风格(二)
--讲授视频
-11.5 软件体系结构风格(三)
--讲授视频
-11.6 软件设计过程
--讲授视频
-11.7 Web系统架构设计
--讲授视频
-11.8 数据库选择策略
--讲授视频
-测验题--作业
-作业题
--html
--html
--html
-作业题--作业
-12.1 交互设计概述
--讲授视频
-12.2 交互设计目标
--讲授视频
-12.3 GUI设计原则
--讲课视频
-12.4 KLM效率模型
--Video
-12.5 Fitts定律
--讲授视频
-12.6 交互设计过程
--讲授视频
-测验题--作业
-13.1 软件测试概念
--讲课视频
-13.2 软件测试类型
--讲课视频
-13.3 软件功能测试
--讲课视频
-13.4 软件性能测试
--讲课视频
-测验题--作业
-14.1 软件部署与交付
--讲课视频
-14.2 软件演化与维护
--讲课视频
-测验题--作业
-第一部分:基础知识
-第二部分:编程与测试(选做)