Pytest 测试框架三部曲(一)
1. 背景介绍
1.1 工具与框架
工具
它是一种用于完成特定的任务的软件或硬件,他们通常是独立的,可以直接使用。不需要太多的结构或依赖关系,工具本身已经提供了一些功能或方法供用户使用。
例如:Postman、Jmeter 等
框架
它是一种更为完整的软件结构,它提供了一套规范、结构、功能,通常包含一些预定义的规则,用户需要按照框架的规范来组织代码。以测试框架为例,它通常是不直接提供特定的测试能力,你需要通过扩展或者安装第三方的工具库来实现。
例如:unittest、pytest、robot framework 等
1.2 单元测试框架和自动化测试框架
单元测试框架是指在软件开发中,对软件的最小单元(函数 / 方法)进行正确性的检查测试。
单元测试框架的主要工作:测试发现、测试执行、测试判断和测试报告。
单元测试框架和自动化测试框架的区别:
- 单元测试框架:主要用于对软件中的单个模块或函数进行测试,它通常关注代码的内部结构和逻辑。单元测试的目的是确保每个单元在独立的情况下能够正常工作,并且符合预期的行为。
- 自动化测试框架:是一种更广泛的测试框架,它可以用于各种类型的测试,如功能测试、性能测试、接口测试等。自动化框架通常会提供一些工具和机制,以便编写和执行测试用例,并管理测试过程。
常见的自动化测试框架:
- Python:unittest、pytest、robot framework(前两个框架主要是聚焦于白盒单元测试,而 robot framework 主要聚焦于系统测试)
- Java:Gunit、TestNG
2. Pytest 介绍
2.1 Pytest 介绍
Pytest 官方文档:https://docs.pytest.org/en/latest/
pytest 是一个非常成熟的全功能的 Python 测试框架,支持自动检索自动、参数化、跳过、标记预期等功能,且可以通过安装插件进行功能的扩展,与 Python 自带的 unittesst 测试框架类似,但是比 unittest 框架使用起来更简洁,功能更强大!
pytest 可以用来做系统测试的自动化,它的特点有:
- 用 Python 编写测试用例,简便易用,比 unittest 更灵活,并且文档也很丰富
- 可以用文件系统目录层次对应手工测试用例层次结构
- 支持参数化,可以灵活挑选测试用例执行,并且可以实现测试用例的跳过以及 reruns 重试失败用例
- 能够支持简单的单元测试和复杂的功能测试
- 灵活的初始化清除机制
- 可以和 allure 生成非常美观的测试报告,也可以和 Jenkins 持续集成
- 支持运行由 nose、unittest 编写的测试 case
- 支持分布式、多线程跑用例
- 可以很好的和 CI 工具结合
- 丰富的插件及社区支持,pytest 有很多强大的插件,可以自定义扩展,并且这些插件能够实现很多实用的操作
- pytest-html:生成 html 格式的自动化测试报告
- pytest-xdist:测试用例分布式执行,多 CPU 分发
- pytest-ordering:用于改变测试用例的执行顺序
- pytest-rerunfailures:用例失败后重跑
- allure-pytest:用于生成美观的测试报告
2.2 Pytest 与 Unittest 的区别
| unittest | pytest | |
|---|---|---|
| 用例编写规则 | 1. 测试文件必须先 import unittest2. 测试类必须继承 unittest.TestCase3. 测试方法必须以 test_ 开头4. 测试类必须要有 unittest.main() 方法 |
1. 测试文件名必须以 test_ 开头或者 _test 结尾(例如:test_ab.py)2. 测试方法必须以 test_ 开头3. 测试类命名以 Test 开头 |
| 用例分类执行 | 默认执行全部用例,也可以用过加载 testsuit 执行部分用例 | 可以通过 @pytest.mark 来标记类和方法,pytest.main 加入参数(”-m”)可以只运行标记的类和方法 |
| 用例前置和后置 | 提供了 setUp/tearDown,只能针对所有用例 |
pytest 中的 fixture 显然更加灵活。可以任意自定义方法函数,只要加上 @pytest.fixture() 这个装饰器,那么被装饰的方法就可以被使用 |
| 参数化 | 需依赖 ddt 库 | 使用 @pytest.mark.parametrize 装饰器 |
| 断言 | 很多断言格式(assertEqual、assertIn、asserTrue 等) | 只有 assert 一个表达式,用起来比较方便 |
| 报告 | 使用 HTMLTestRunner 库 | 有 pytest-HTML、allure 插件 |
| 失败重跑 | 无此功能 | pytest 支持用例执行失败重试,使用 pytest-rerunfailures 插件 |
2.3 为何用 pytest?
任何测试框架,最原始的写法:
- 没有测试报告
- 不好维护
大部分开发语言本身提供了自带的测试框架,例如 unittest:
- 规范了用例代码的写法
- 一个用例用一个方法概括
- 提供了方便执行与统计的功能
- 官方提供自带的,用起来功能不是很强大,没那么方便
当前流行的测试框架 - pytest
- 不是 python 自带的,需要自行安装。
1 | pip install pytest |
- pytest 和 unittest 相比,更加的规范和宽松,函数定义只需 test 开头或结尾即可。
1 | pytest test_app_01.py |
- unittest 能做的,pytest 也能做,并且更强大。
2.4 Pytest 应用
pytest 本质很简单,它只判断 def 函数中是否会报错或者 assert 断言不通过,不管里面内容如何。
因此,pytest 是自动化测试的基石,我们可以用 pytest 实现不同场景下的测试。
例如下面代码,上面为 UI 的自动化测试代码,而下面的则为接口自动化测试代码,但是它们都是基于 pyest 的。
它们只需在函数中通过断言判断结果是否符合预期即可,如果报错则 pytest 就会识别出来,并返回该用例不通过。
Pytest 插件列表:https://docs.pytest.org/en/latest/reference/plugin_list.html
所以这就出现了 pytest 与各种框架的组合,例如:pytest + selenium(UI / web 自动化)、pytest + requests(HTTP / 接口自动化)、pytest + appium(AppUI / APP 自动化)、pytest + websocket 等,什么类别的自动化代码取决于你在 def 函数中的内容。
3. Pytest 快速上手
3.1 安装
直接执行如下命令即可安装 pytest:
1 | pip install pytest |
安装后可以验证是否安装成功:
1 | pytest --version |
但是,我们还需要安装很多插件,如果一个个安装就比较麻烦,我们可以在根目录下创建一个 requirements.txt 文件,来存放所有需要安装的插件。
注意: 前面讲解的部分命令中涉及到了下面的插件,所以推荐都事先安装好避免报错~
1 | pytest |
Pytest 插件列表:https://docs.pytest.org/en/latest/reference/plugin_list.html
然后通过如下的命令来下载该文件中的所有插件。
1 | pip install -r requirements.txt |
运行原理
pytest 如何知道你哪些代码是自动化的测试用例?官方文档给出了 pytest 寻找测试项的具体规则:
- 如果未指定命令行参数,则从 testpath(如果已配置)或当前目录开始收集;如果命令行参数,指定了目录、文件名或 node id 的任何组合,则按参数来找。
- 寻找过程会递归到目录中,除非它们匹配上 norecursedirs。
- 在这些目录中,搜索由其测试包名称导入的
test_*.py或*_test.py文件。- 从这些文件中,收集如下测试项:
- test 为前缀的函数
- Test 为前缀的类里面的 test 为前缀的方法
3.2 测试用例代码
默认命名规则:
- 文件和模块名称必须以 test_ 开头或者以 _test 结尾,目录名称可以不遵循该规则。
- 测试类名称必须以 Test 开头,且类中不能有 init 方法。
- 测试方法必须以 test 开头。
- 名称允许包含中文。
比如,我们创建一个 文件名为 test_错误登录.py ,放在目录 workspace\cases\登录 下面。
其中 worksapce 是我们创建的自动化项目根目录,内容如下:
1 | class Test_错误密码: |
如果我们把测试用例存放在类中,则类名必须以 Test 为前缀的 类,用例对应的方法必须以 test 为前缀的方法。pytest 中用例的检查点直接用 Python 的 assert 断言,assert 后面的表达式结果为 True ,就是检查点通过;结果为 False ,就是检查点不通过。
3.3 运行测试
3.3.1 命令行的方式
在 terminal 终端,进入到根目录(这里是 workspace),执行命令 pytest 或 python -m pytest。
注意:
最好使用
python -m pytest的方式执行,因为我们测试用例经常需要导入项目目录的库文件,如果直接使用pytest执行会出现 python 解释器搜索不到库文件的问题:ModuleNotFoundError: No module named 'xxxx'。而使用
python -m pytest的方式运行 python,则会自动把当前工作目录作为模块的搜索路径,避免了上述问题的发生。
当然也可以指定要测试的目录即 pytest cases,上面代码运行结果如下。
1 | pytest |
结果显示一共找到了 3 个测试项,其中 2 个执行通过,1 个不通过,并且会显示出执行不通过的用例以及细节。
另外,打印的结果中,文件名称后面的绿色小点表示该条用例通过,红色字母 F 则表示该条用例不通过。
3.3.2 主函数的方式
1 | if __name__=='__main__' |
在主函数中也可以使用参数,例如下面我想增加重试的机制,在命令行中效果如下:
1 | python -m pytest cases -sv --reruns 2 --reruns-delay 1 |
通过主函数方式执行的话,则效果如下:
1 | if __name__=='__main__' |
3.3.3 全局配置文件 pytest.ini 的方式
官网文档:https://docs.pytest.org/en/latest/reference/reference.html#ini-options-ref
pytest.ini 这个文件是 pytest 单元测试框架的核心配置文件。它可以修改 pytest 的默认行为,通过读取配置信息,从而改变 pytest 运行方式,按指定的方式来运行。比如使用 pytest.ini 文件可以破除默认 pattern,命名测试文件、搜索规则等。
- 位置:一般放在项目的根目录
- 作用:改变 pytest 默认的行为
- 运行的规则:不管是主函数的模式运行,还是命令行的模式运行,都会去读取这个配置文件
1 | [pytest] |
现在我们可以修改一下用例中类和方法的名称,然后观察一下按照默认规则运行和修改规则后运行的结果如何。
1 | class aaa_错误密码: |
3.4 参数详解
- -s
从上面代码中可以发现,存在 print 打印语句,但是终端中并没有打印相关信息,这是因为 pytest 会截获 print 打印的内容。如果希望显示代码中 print 的内容的话,则可以在 pytest 后加上参数 -s。
1 | python -m pytest -s |
- -v
如果希望得到更详细的执行信息,包括每个测试类、测试函数的名字,可以加上参数 -v。
1 | python -m pytest -sv |
- 其它参数
- -x:只要有一个用例错误就会停止执行
- –maxfail NUM:出现 NUM 个用例失败就会停止执行
- -k:根据测试用例的部分字符串指定测试用例(包括文件名、类名、方法名等)
python -m pytest -k "1002":只会执行文件名、类名、方法名等的名称中包含 1002 字符的用例







