pytest--解决 scope=session 的 fixture 在多进程运行情况下仍然只运行一次
前言
在多进程的情况下,每个子进程都会是一个session,里面都会执行一次session级别的fixture,那么如果有些数据是随机生成的,比如生成token、密钥等数据,那么在多进程执行,不同进程里面,得到的token或者密钥值那就不一致了。就可能导致用例执行的失败,那么直接举个例子吧
示例
比如随机生成token:工程目录结构如下:
根目录下test_case.py:
import pytest #参数化,传入参数 @pytest.mark.parametrize('n',list(range(3))) def test_get_info(login,n): print('===获取用户个人信息===',n) name,token = login print(f'用户名:{name},token:{token}')
根目录下conftest.py:
import random import pytest @pytest.fixture(scope='session') def login(): name = 'admin' token = str(random.random()) return name,token
test_baidu文件夹下test_case1.py:
import pytest @pytest.mark.parametrize('n',list(range(2))) def test_case1_1(open_baidu,n):print('===baidu 执行测试用例test_case1_1====',n) @pytest.mark.parametrize('n',list(range(2))) def test_case1_2(open_baidu,n):print('===baidu 执行测试用例test_case1_2====',n)
test_baidu文件夹下test_case2.py:
def test_01(): print('====1===========') class Test_01: def test_02(self): print('=======2=============')
test_baidu文件夹下conftest.py:
import pytest @pytest.fixture(scope='module') def open_baidu(login): name,token = login print(f'用户{name}打开了百度,token:{token}')
使用pytest-xdist多进程执行测试用例,与html报告搭配查看输出。只使用3个进程去跑9个测试用例
根目录执行命令:pytest -n 3--html=report.html
输出结果如下:
可以看到分配了3个进程去跑,随机得到3种token值,也就是证明了scope='session'级别的login的fixture被调用了3次,所以才会产生3种token值
但这与我们所希望的不符合,因为单进程去跑,session级别的fixture只会在所有用例执行前后才会运行一次。
实现session级别的fixture在多进程中只运行一次
实现思路:如果是分布式运行的话,使用FileLock来给缓存文件上锁,第一个进程调用写入数据后,其余进程也可以访问该缓存文件,但只读取,就可以保证所有进程得到同样的数据。如果是单进程运行,那么可以判断worker_id是否是“admin”,如果是的话,照常生成token,不会出现session级别的fixture运行多次的问题
那么接下来就对根目录下的conftest.py进行调整:
import json import os import random import pytest from filelock import FileLock @pytest.fixture(scope='session') #tmp_path_factory是一个session级别的fixture,每次执行只会创建一个临时目录 def login(tmp_path_factory,worker_id): name = 'admin' #单机运行,则运行这里代码 if worker_id == 'master': token = str(random.random()) print(f'token:{token}') #写入os的环境变量中 os.environ['token'] = token return name,token # 分布式运行 # 获取所有子节点共享的临时目录 print(tmp_path_factory) root_tmp_dir = tmp_path_factory.getbasetemp().parent print(root_tmp_dir) fn = root_tmp_dir / 'data.json' print(fn) #FileLock来给缓存文件上锁,第一个进程调用写入数据后,其余进程也可以访问该缓存文件,但只读取,就可以保证所有进程得到同样的数据 with FileLock(str(fn) + '.lock'): if fn.is_file(): #缓存文件中读取数据,例如token token = json.loads(fn.read_text()) print(f'读取缓存文件,token:{token}') else: #第一次生成缓存文件 token = str(random.random()) print(f'fixture请求登陆端口,获取token:{token}') fn.write_text(json.dumps(token)) print(f'首次执行,token:{token}') #token保存os的环境变量 os.environ['token'] = token return name,token
输出结果:
可以看到所有的token值都固定了,也就证明token生成只执行了一次,并写入到缓存文件中,剩下的进程则是去读取缓存文件中的token值。并不是所有的测试用例都会去读取缓存文件,而是根据进程去访问缓存文件。这也同样做到了所有进程下读取到数据是一致的
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 提示词工程——AI应用必不可少的技术
· 地球OL攻略 —— 某应届生求职总结
· 字符编码:从基础到乱码解决
· SpringCloud带你走进微服务的世界