自动化框架 pytest
- 一. pytest 介绍
- 二. pytest 安装
- 三. pytest 运行规则
- 四. pytest 命令参数
- 五. pytest 配置文件
- 六. pytest 前后置问题
- 七. pytest 断言
- 八. pytest 参数化
- 九. pytest fixture
- 1. fixture 基本使用
- 2. fixture 嵌套使用
- 3. 请求多个 fixture
- 4. yield fixture
- 5. fixture 参数解析
一. pytest 介绍
支持 Python 语言的接口自动化框架有很多,以下是支持 Python 的接口自动化主流框架对比分析:
pytest 官方文档:https://docs.pytest.org/en/stable/getting-started.html
pytest 是一个非常流行且高效的 Python 测试框架,它提供了丰富的功能和灵活的用法,使得编写和运行测试用例变得简单而高效。
为什么选择 pytest:
- 简单易用:pytest 的语法简洁清晰,对于编写测试用例非常友好,几乎可以在几分钟内上手。
- 强大的断言库:pytest 内置了丰富的断言库,可以轻松地进行测试结果的判断。
- 支持参数化测试:pytest 支持参数化测试,允许使用不同的参数多次运行同一个测试函数,这大大提高了测试效率。
- 丰富的插件生态系统:pytest 有着丰富的插件生态系统,可以通过插件扩展各种功能,比如覆盖率测试、测试报告生成 (如 pytest-html 插件可以生成完美的 HTML 测试报告)、失败用例重复执行 (如 pytest-rerunfailures 插件) 等。此外,pytest 还支持与 selenium、requests、appinum 等结合,实现 Web 自动化、接口自动化、App 自动化测试。
- 灵活的测试控制:pytest 允许跳过指定用例,或对某些预期失败的 case 标记成失败,并支持重复执行失败的 case
二. pytest 安装
安装 pytest 8.3.2 要求 python 版本在 3.8 及以上。
若 python 版本低于 3.8,可参考表格不同的 pytest 版本支持的 python 版本:
安装好 pytest 后,确认 pycharm 中 python 解释器已经更新,来看一下没有 pytest 框架和有 pytest 框架编写代码的区别:
两张对比图可以明显看出来,未安装 pytest 框架的情况下需要编写 main 函数,在 main 函数中手动调用测试用例 test01。安装了 pytest 框架后方法名前有直接运行标志。然而并不是所有的方法都可以直接运行,需要遵循 pytest 中的用例命名规则。
三. pytest 运行规则
当满足以上要求后,可通过命令行参数 pytest 直接运行符合条件的用例:
注意:Python 类中不可以添加 init 方法
由于 pytest 的测试收集机制,测试类中不可以定义__init__方法。pytest 采用自动发现机制来收集测试用例。它会自动实例化测试类并调用其所有以 test 结尾的方法作为测试用例。如果测试类中定义了__init__方法,那么当 pytest 实例化该类时,__init__方法会被调用,这可能会掩盖测试类的实际测试逻辑,并引入额外的副作用,影响测试结果的准确性。
若测试类中存在初始化操作该采取什么方案?
为了避免使用__init__方法,建议在 pytest 中使用其他替代方案,如使用 setUp() 和 tearDown() 方法、使用类属性、使用 fixture 函数。
四. pytest 命令参数
pytest 提供了丰富的命令行选项来控制测试的执行,以下是常用的 pytest 命令行参数及其使用说明:
在当前目录及子目录中搜索并运行测试:pytest
增加输出的详细程度:pytest -v
显示测试总的 print 语句:pytest -s
合并操作:pytest -v -s / pytest -vs
运行指定目录下的所有测试:pytest .\tests\test_01.py
指定特定的类名中的测试用例:pytest .\tests\test_01.py::Tests_01::tests_01
问题:当我们既要详细输出,又要指定文件时,命令会非常长,而且每次运行都需要手动输入,如何解决?
此时需要将相关配置参数统一放到 pytest 配置文件中。
五. pytest 配置文件
在当前项目下创建 pytest.ini 文件,该文件为 pytest 的配置文件,以下为常见的配置选项:
示例:详细输出 tests 目录下,以 test_ 开头的文件名,以 Test 开头的类名,以 test_ 开头的方法名,的所有用例:
pytest.ini 文件通常位于项目的根目录下,通过在 pytest.ini 中定义配置项,可以覆盖 pytest 的默认行为,以满足项目的需求。
六. pytest 前后置问题
遗留问题:使用 pytest 框架,测试类中不可以添加__init()__方法,如何进行数据的初始化?
在测试框架中,前后置是指在执行测试用例前和测试用例后执行一些额外的操作,这些操作可以用于设置测试环境、准备测试数据等,以确保测试的可靠性 (例如:提前申请堆区空间,打开文件)
pytest 框架提供三种方法做前后置的操作:
- setup_method 和 teardown_method:这两个方法用于类中的每个测试方法的前置和后置操作。
- setup_class 和 teardown_class:这两个方法用于整个测试类的前置和后置操作。
- fixture:这是 pytest 推荐的方式来实现测试用例的前置和后置操作,fixture 提供了更灵活的控制和更强大的功能 (该内容后续在后面介绍)
在执行每个测试方法之前都会调用 setup_method,在执行完每个测试方法之后都会调用 teardown_method,如下:
在执行测试方法之前只会调用一次 setup_class,在执行完所有测试方法之后只会调用一次 teardown_class,如下:
七. pytest 断言
断言 (assert) 是一种调试辅助工具,用于检查程序的状态是否符合预期。如果断言失败 (即条件为假),Python 解释器将抛出一个 AssertionError 异常,断言通常用于检测程序中的逻辑错误。
pytest 允许你在 Python 测试中使用标准的 Python assert 语句来验证预期和值。
基本数据类型的断言:
数据结构断言:
函数断言:
接口返回值断言
免费学习的 API 资源:http://jsonplaceholder.typicode.com/
返回 JSON 格式的断言:
返回 HTML 格式的断言:
八. pytest 参数化
参数化设计是自动化设计中的一个重要组成部分,它通过定义设计参数和规则,使得设计过程更加灵活和可控,pytest 中内置的 @pytest.mark.parametrize 装饰器允许对测试函数的参数进行参数化。
使用的是虚拟环境的话,需要取消虚拟环境包的排除,如下:
用例使用参数化
@pytest.mark.parametrize 装饰器定义了三个不同的 (input, expect) 元组,以便 eval 函数将依次使用它们运行三次,也可以在类或模块上使用 parametrize 标记,这将使用参数集调用多个函数。
类使用参数化
要对模块中的所有测试进行参数化,你可以将 pytestmark 全局变量赋值:
除了使用 @pytest.mark.parametrizeparametrize 添加参数化外,pytest.fixture() 允许对 fixture 函数进行参数化。
自定义参数化数据源
九. pytest fixture
pytest 中的 fixture 是一种强大的机制,用于提供测试函数所需的资源或上下文,它可以用于设置测试环境、准备数据等,以下是 fixture 的一些核心概念和使用场景:
1. fixture 基本使用
使用与不使用 fixture 标记
未标记 fixture 方法的调用与 fixture 标记的方法调用完全不一样,前者需要在方法体中调用,而后者可以将函数名作为参数进行调用,测试脚本中存在的很多重复的代码、公共的数据对象时,使用 fixture 最为合适。
访问博客列表页和详情页之前都需要执行登录操作
通过使用 @pytest.fixture 装饰器来告诉 pytest 一个特定函数是一个 fixture,通过运行结果可见,在执行列表页和详情页之前都会先执行 login 方法。
2. fixture 嵌套使用
测试不必局限于单个 fixture,它们可以依赖于您想要的任意数量的 fixture,并且 fixture 也可以使用其他 fixture,pytest 最大优势之一是其极其灵活的 fixture 系统,它允许我们将测试的复杂需求简化为更简单和有组织的函数,我们只需要每个函数描述它们所依赖的事物。
3. 请求多个 fixture
测试和 fixture 不仅限于一次请求单个 fixture,它们可以请求任意多个。
4. yield fixture
当我们运行测试时,我们希望确保它们能够自我清理,以便它们不会干扰其他测试 (同时也避免留下大量测试数据来膨胀系统),pytest 中的 fixture 提供了一个非常有用拆卸系统,它允许我们为每个 fixture 定义具体的清理步骤。
“yield fixture” 使用 yield 而不是 return,有了这些 fixture,我们可以运行一些代码,并将对象返回给请求的 fixture/test,就像其他 fixture 一样,唯一的不同是:
- return 被替换为 yield
- 该 fixture 的任何拆卸代码放置在 yield 之后。
一旦 pytest 确定了 fixture 的线性顺序,它将运行每个 fixture 直到它返回或 yield,然后继续执行列表中的下一个 fixture 做同样的事情。测试完成后,pytest 将逆向遍历 fixture 列表,对于每个 yield 的 fixture,运行 yield 语句之后的代码。
创建文件句柄与关闭文件
5. fixture 参数解析
- scope 参数用于控制 fixture 的作用范围,决定了 fixture 的生命周期,存在如下值
- function (默认):每个测试函数都会调用一次 fixture
- class:在同一个测试类中共享这个 fixture
- module:在同一个测试模块 (文件) 中共享这个 fixture
- session:整个测试会话中共享这个 fixture
- autouse 参数默认为 False,如果设置为 True,则每个测试函数都会自动调用该 fixture,无需显式传入。
- params 参数用于参数化 fixture,支持列表传入,每个参数值都会使 fixture 执行一次,类似于 for 循环。
- ids 参数与 params 配合使用,为每个参数化实例指定可读的标识符 (给参数取名字)
- name 参数用于为 fixture 显式设置一个名称,如果使用了 name,则在测试函数中需要使用这个名称来引用 fixture (给 fixture 取名字)
参数 scope
@pytest.fixture(scope = “function”):
@pytest.fixture(scope = “class”):
@pytest.fixture(scope = “module”):
当测试用例放在不同的文件时,以上的写法将会出现找不到 fixture 的问题,解决办法就是需要将 fixture 放到配置文件里,实现多个文件共享。
conftest.py 和 @pytest.fixture 结合使用实现全局的前后置应用,@pytest.fixture 与 conftest.py 文件结合使用,可以实现在多个测试模块 (.py 文件) 中共享前后置操作,这种结合的方式使得可以在整个测试项目中定义和维护通用的前后置逻辑,使测试代码更加模块化和可维护,规则如下:
- conftest.py 是一个单独存放的夹具配置文件,名称是固定的不能修改。
- 你可以在项目中的不同目录下创建多个 conftest.py 文件,每个 conftest.py 文件都会对其所在目录及其自目录下的测试模块生效。
- 在不同模块的测试中需要用到 conftest.py 的前后置功能时,不需要做任何的 import 导入操作。
- 作用:可以在不同的 .py 文件中使用同一个 fixture 函数。
错误的写法:
正确的写法:
@pytest.fixture(scope = “session”):
结论:
- scope 默认为 function,这里的 function 可以省略不写,当 scope=“function” 时,每个测试函数都会调用一次 fixture
- scope=“class” 时,在同一个测试类中,fixture 只会在类中的第一个测试函数开始前执行一次,并在类中的最后一个测试函数结束后执行清理。
- 当 scope=“moudle”、scope=“session” 时可用于实现全局的前后置应用,这里需要多个文件的配合。
参数 autouse
autouse 默认为 False,即当前的 fixture 需要手动显示调用,在该案例之前我们默认使用的都是 autouse=False,当 autouse=True 时,fixture 会在所有测试函数执行之前自动调用,无论这些测试函数是否显式地引用了该 fixture
参数:params
前面 pytest 中通过 @pytest.mark.parametrize 实现参数化:
通过 fixture 也可以实现参数化:
如果测试场景主要涉及简单的参数传递,且不需要复杂的资源管理,建议使用 parametrize,因为它更简单直接。如果测试需要动态加载外部数据,或者需要管理复杂的测试资源 (如数据库连接、文件操作等),建议使用 fixture。在某些情况下,也可以结合使用 parametrize 和 fixture,以充分利用两者的优点。
总结来说:parametrize 更适合简单场景,而 fixture 更适合需要动态数据和资源管理的复杂场景。