fixture 是在测试函数运行前后由 pytest 执行的外壳函数,可以定制包括定义传入测试中的数据集,配置测试前系统的初始状态、为批量测试提供数据源等等。fixture 是 pytest 用于将测试前后进行预备、清理工作的代码分理处核心测试逻辑的机制。
@pytest.fixture()
装饰器声明函数是一个 fixture,如果测试函数的参数列表中包含 fixture 名,那么 pytest 就会检测到,并在运行测试函数之前运行它,fixture 可以返回数据给测试函数。其优势在于编写测试函数时可以只考虑核心测试逻辑,而不需要考虑测试前的准备工作。正因为 fixture 如此重要所以整理一篇关于 fixture 用法的文章,使得能够更好更优雅地使用它。
通过 conftest.py 共享 fixture
fixture 可以单独放在测试文件中,但是如果需要多个测试文件共享 fixture,那么可以在其公共目录下新建一个 conftest.py 文件,将 fixture 放在其中。conftest 可以看作 pytest 的一个本地插件库,不允许使用 import conftest
的用法,conftest 的作用范围是其所在目录及其子目录。
使用 fixture 执行配置及销毁逻辑
fixture 函数会在测试函数之前运行,但是如果 fixture 函数包含 yield,那么会在 yield 除停止并转而运行测试函数,等测试函数执行完毕后再回到该 fixture 继续执行 yield 后面的代码。可以将 yield 前面的代码看作是 setup,yield 后面的部分看作是 teardown 的过程。无论是测试函数中发生了什么是成功还是失败或者 error 等情况,yield 后面的代码都会被执行,yield 中的返回数据也可以在测试函数中使用。
# content of conftest.py
import smtplib
import pytest
@pytest.fixture(scope="module")
def smtp_connection():
smtp_connection = smtplib.SMTP("smtp.gmail.com", 587, timeout=5)
yield smtp_connection # provide the fixture value
print("teardown smtp")
smtp_connection.close()
如上所示,只需要在测试函数的参数中添加 smtp_connection 即可,def smtp_connection()
中的 smtp_connection 返回值也可以在测试函数中使用。在测试函数执行完毕后继续执行 yield 后面的语句。
使用 --setup-show 回溯 fixture 执行过程
pytest 提供 --setup-show 可以看到测试过程中 fixture 的过程。结果中的 S 表示 session 即会话级别,M 表示 module 级别,C 表示 class 级别,F 表示 function 级别。
fixture 只能使用同级别或者比自己级别更高的 fixture。比如,函数级别的 fixture 使用同级别的 fixture 也可以使用类级别、模块级别、会话级别的 fixture,但是不可以反过来。
fixture 的作用范围
fixture 有一个 scope 可选参数,默认为 function ,可选值为 function、class、module、session,范围依次增加。function 表示每个测试函数只需要运行依次,配置代码在测试用例运行之前运行,销毁代码在测试用例之后运行。class 表示每个测试类运行依次,无论测试类中有多少方法,都可以共享这个 fixture。module 表示每个模块运行一次,无论模块里有多少测试函数、类方法或者其他 fixture 都可以共享这个 fixture。session 表示每次会话运行只需要运行一次,一次 pytest 会话中的所有测试函数、方法都可以共享这个 fixture。
使用 usefixtures 指定 fixture
可以在测试函数前使用 @pytest.mark.usefixtures("fixture1","fixture2")
标记测试函数或者测试类。与在测试方法中添加 fixture 参数差不多,但是使用 usefixtures 不能使用 fixture 的返回值。
autouse 选项
对于那些不依赖于任何系统状态或者外部数据,又需要多次运行的代码,可以在 fixture 中添加 autouse=True
选项,例如 @pytest.fixture(autouse=True, scope="session")
。
但是,如果可以的话,尽量应当选择参数传递或者 usefixtures 的方法而不是 autouse。autouse 会让测试函数逻辑看上去没有那么清晰,更像是一个特例。
为 fixture 重命名
通常来说使用 fixture 的测试函数会将 fixture 的函数名作为参数传递,但是 pytest 也允许将 fixture 重命名。只需要使用@pytest.fixture(name="new")
即可,在测试函数中使用该 fixture 时只需要传入 new 即可。
fixture 的参数化
在 pytest 中有一个内建的 fixture 叫做 request,代表 fixture 的调用状态。request 有一个字段 param,可以使用类似@pytest.fixture(param=tasks_list)
的方式,在 fixture 中使用 request.param
的方式作为返回值供测试函数调用。其中 tasks_list 包含多少元素,该 fixture 就会被调用几次,分别作用在每个用到的测试函数上。
由于没有指定 id,所以在输出时 pytest 会以 fixture 名加上数字作为标识,fixture 也可以指定 id,例如@pytest.fixture(param=tasks_list,ids=task_ids)
ids 可以是列表,也可以是函数供 pytest 生成 task 标识
总结
本篇主要是简单整理下 fixture 的用法,在日常工作中都是比较常用的。fixture 功能强大,是 pytest 的优点之一,利用好了是测试的利器。下篇再整理一下 pytest 的内置 fixture