pytest中如何在测试脚本模块层级定义供模块级fixture使用的变量
问题根因
- 当前用跨模块全局变量存储字符串的逻辑存在两个核心问题:
- pytest测试收集阶段会按顺序执行所有测试脚本的顶层代码,所有脚本调用
env.set_my_string()时都会修改同一个全局_tmp_str,收集流程结束后,全局变量会被最后一个被收集的测试脚本覆盖,等模块作用域的fixture执行时,拿到的自然不是当前测试脚本专属的值。 - 现有
env_var.py的set_my_string函数缺少global _tmp_str声明,当前写法里_tmp_str = in_str是函数内部的局部变量,根本不会修改全局状态,就算单跑一个测试脚本,get_my_string拿到的永远是空字符串。
- pytest测试收集阶段会按顺序执行所有测试脚本的顶层代码,所有脚本调用
解决方案
完全不需要跨模块的全局中间存储,利用pytest内置的模块上下文能力,把字符串绑定到每个测试脚本自身的模块对象上,从根源上避免跨模块污染,同时完全兼容你现有“通过函数调用设置字符串、不直接写模块变量赋值”的需求。
步骤1:改造env_var.py的存储逻辑
把原来存全局变量的逻辑,改成把值绑定到调用set_my_string的测试模块自身上,不再使用跨模块的全局变量:
import sys def set_my_string(in_str): # 定位调用当前函数的测试模块对象 caller_module_name = sys._getframe(1).f_globals["__name__"] caller_module = sys.modules[caller_module_name] # 把值挂到调用方的模块对象上,不做全局状态修改 caller_module.my_custom_str = in_str
改造后你的测试脚本(比如my_script1.py)的写法完全不需要变动,和现有代码保持一致:
import env_var as env # 模块顶层调用设置函数,和原有逻辑完全一致 env.set_my_string('This string will be used in the module fixture') def test_first(): pass
步骤2:改造pytest配置文件的fixture逻辑
利用pytest fixture内置的request对象,直接读取当前正在执行的测试模块上绑定的字符串值:
import pytest @pytest.fixture(scope='module', autouse=True) def script_handler(request): # request.module 直接指向当前执行流程对应的测试脚本模块 current_test_module = request.module # 读取模块上绑定的自定义字符串,无值时可配置默认兜底值 txt = getattr(current_test_module, "my_custom_str", "默认兜底字符串") # 后续使用txt执行业务逻辑 yield
注:如果你当前的pytest配置文件命名为
conf_test.py,只需保证该文件在pytest的扫描路径内即可,无需强制修改为conftest.py的默认命名。
方案优势
- 所有字符串值都是和测试模块本身绑定的,每个模块的属性存储完全独立,不会出现跨模块覆盖的问题,fixture拿到的永远是当前测试脚本设置的专属值。
- 完全兼容现有代码结构,不需要修改测试脚本里的调用写法,满足“通过函数而非直接给变量赋值”的要求。
- 没有残留的全局状态,并行跑测试、批量跑目录下所有测试脚本时都不会出现值串扰的问题。
内容的提问来源于stack exchange,提问作者someuser
相关产品推荐
相关产品推荐

