在 Pytest 框架中,Fixture(固件) 绝对是最核心、最强大的功能,没有之一。如果说 parametrize 是 Pytest 的"心脏"(负责驱动数据),那么 Fixture 就是 Pytest 的"神经系统"(负责提供测试所需的全部外部资源和环境)。
python
@pytest.fixture
def login_token():
print("① 去数据库查用户,生成 token") # 前置
yield "abc123" # 把 token 交给用例
print("③ 用例跑完了,删除这个 token 缓存") # 后置(无论用例成功还是失败,都会执行!)
def test_order(login_token):
print("② 拿着 token 下单")
assert "abc123" in login_token
上面这段代码的打印顺序是:① → ② → ③
把 yield 换成 return 的执行顺序:① → ②(③ 永远不会执行)
Pytest 的执行指针(Instruction Pointer)跳转顺序:
第 0 步(准备):Pytest 扫描发现 test_order 需要 login_token 参数,决定先去 login_token 拿值。
指针跳转到第 ② 行:Pytest 调用 login_token() 函数。
指针进入第 ③ 行:执行 print("① ...")(此时控制台输出 ①)。
指针到达第 ④ 行:遇到 yield "abc123",函数在此处暂停(冻结),把 "abc123" 扔出来。
指针跳出 login_token:Pytest 拿到了返回值。
指针跳转到第 ⑥ 行:Pytest 拿着 "abc123",现在才开始调用 test_order()!
指针进入第 ⑦ 行:执行 print("② ...")(控制台输出 ②)。
指针进入第 ⑧ 行:执行断言。
test_order 执行完毕:Pytest 发现调用结束了。
指针再次跳回第 ④ 行(yield 的下一行):Pytest 恢复之前暂停的 login_token 函数,执行第 ⑤ 行 print("③ ...")(控制台输出 ③)。
上面代码中assert "abc123" in login_token 这句代码 还会再调用一遍login_token函数吗
绝对不会! 这句断言 不会再调用一遍 login_token 函数
- 执行时的真相(变量与函数的区别)
在 def test_order(login_token): 这一行中,login_token 是函数的形参(参数名)。
当 Pytest 要运行这个测试时,它做的事情是:
提前执行 Fixture:它先跑去执行 login_token 这个函数,拿到返回值 "abc123"(如果在 yield 情况下,执行到 yield 暂停)。
参数注入(赋值):Pytest 把拿到的结果 "abc123" 直接赋值给 test_order 的参数 login_token。
执行用例:此时在函数体内,login_token 已经不再是一个函数,而是一个实实在在的字符串变量,它的值就是 "abc123"。
所以,你在断言里写 assert "abc123" in login_token,在 Python 看来,这行代码实际上等价于assert "abc123" in "abc123"
@pytest.fixture 上面代码中这一段代码是什么作用
如果你把 @pytest.fixture 这行删掉 会报错。 Python 会认为 test_order 是一个普通函数,需要调用者手动传参。但你在调用时没传,所以报错。
加上 @pytest.fixture 后:Pytest 在启动时会扫描所有被这个装饰器标记的函数,把它们记录在一个"内部字典(注册表)"里。当它看到 test_order 需要一个名为 login_token 的参数时,就会去注册表里找,找到后自动执行该函数,把返回值注入进来。这就实现了"依赖注入(DI)"------你不需要手动调用,框架帮你把"原料"送到手上。
@pytest.fixture 是 Pytest 依赖注入的核心实现。它在框架启动时将被装饰函数注册为资源提供者,通过参数名匹配的方式,在测试函数执行前自动调用该函数获取资源(如 session、数据库连接),并支持通过 yield 实现资源的前置分配与后置回收,从而解决了测试环境的初始化和清理问题。
被 @pytest.fixture 装饰的是 login_token 函数,而不是 test_order 函数。
test_order 被称为"测试用例(Test Case)"或"测试函数(Test Function)"。
核心原理:
Pytest 在启动时(收集阶段)做了一件极其重要的事:它遍历所有以 test_ 开头的函数,把它们从"普通函数"注册进自己的"待执行队列"中,并打上"测试用例"的标签。
当 Pytest 准备执行 test_order 时,它会检查这个函数需要哪些参数(这里需要 login_token)。Pytest 去自己的"注册表"里找,发现 login_token 是一个被 @pytest.fixture 标记的"供应商"。
装饰器是什么?
装饰器(Decorator) @pytest.fixture
被装饰的函数 def login_token():
消费者 def test_order(login_token):