pytest--xdist流程原理和执行顺序(--dist)
xdist原理和流程
xdist 的分布式类似于一主多从的结构,master 机负责下发命令,控制 slave 机;slave 机根据 master 机的命令执行特定测试任务。
在 xdist 中,主是 master,从是 workers。
分布式测试的原理:
(1)xdist 会产生一个或多个 workers,workers 都通过 master 来控制
(2)每个 worker 负责执行完整的测试用例集,然后按照 master 的要求运行测试,而 master 机不执行测试任务
分布式测试的流程:
1、创建 worker
(1)master 会在总测试会话(test session)开始前产生一个或多个 worker;
(2)master 和 worker 之间是通过 execnet 和网关来通信的;
(3)实际编译执行测试代码的 worker 可能是本地机器也可能是远程机器。
2、收集测试用例
(1)每个 worker 类似一个迷你型的 pytest 执行器;
(2)worker 会执行一个完整的 test collection 过程(收集所有测试用例的过程);
(3)然后把测试用例的 ids 返回给 master;
(4)master 是不会执行任何测试用例集的。
注:所以为什么脚本代码里有打印语句(print)通过分布式测试时结果没有输出用例的打印内容,因为主机并不执行测试用例,PyCharm 相当于一个 master。
3、master 检测 workers 收集到的测试用例集
(1)master 接收到所有 worker 收集的测试用例集之后,master 会进行一些完整性检查,以确保所有 worker 都收集到一样的测试用例集(包括顺序);
(2)如果检查通过,会将测试用例的 ids 列表转换成简单的索引列表,每个索引对应一个测试用例的在原来测试集中的位置;
(3)所有的节点都保存着相同的测试用例集,并且使用这种方式可以节省带宽,因为 master 只需要告知 workers 需要执行的测试用例对应的索引,而不用告知完整的测试用例信息。
4、测试用例分发
--dist-mode 选项
each:master 将完整的测试索引列表分发到每个 worker。
load:master 将大约25%的测试用例以轮询的方式分发到各个 worker,剩余的测试用例则会等待 workers 执行完测试用例以后再分发。
注:可以使用 pytest_xdist_make_scheduler 这个 hook 来实现自定义测试分发逻辑。
5、测试用例的执行
(1)workers 重写了 pytest_runtestloop(pytest 的默认实现是循环执行所有在 test session 这个对象里面收集到的测试用例);
(2)但是在 xdist 里, workers 实际上是等待 master 为其发送需要执行的测试用例;
(3)当 worker 收到测试任务, 就顺序执行 pytest_runtest_protocol;
(4)值得注意的一个细节是:workers 必须始终保持至少一个测试用例在任务队列里, 以兼容 pytest_runtest_protocol(item, nextitem)hook 的参数要求,为了将 nextitem 传给 hook;
(5)worker 会在执行最后一个测试项前等待 master 的更多指令;
(6)如果它收到了更多测试项, 那么就可以安全的执行 pytest_runtest_protocol,因为这时 nextitem 参数已经可以确定;
(7)如果它收到一个 "shutdown" 信号, 那么就将 nextitem 参数设为 None, 然后执行 pytest_runtest_protocol。
6、测试用例再分发
--dist-mode=load
(1)当 workers 开始/结束执行时,会把测试结果返回给 master,这样其他 pytest hook 比如(pytest_runtest_protocol 和 pytest_runtest_protocol 就可以正常执行);
(2)master 在 worker 执行完一个测试后,基于测试执行时长以及每个 work 剩余测试用例综合决定是否向这个 worker 发送更多的测试用例。
7、测试结束
(1)当 master 没有更多执行测试任务时,它会发送一个 "shutdown" 信号给所有 worker;
(2)当 worker 将剩余测试用例执行完后退出进程;
(3)master 等待所有 worker 全部退出;
(4)此时仍需要处理诸如 pytest_runtest_logreport 等事件。
以上原文链接:https://blog.csdn.net/wangmcn/article/details/121080902
获取测试用例打印
使用xdist分布式运行测试用例时,可以发现测试用例中的print并没有在pycharm的终端打印出来。这个原因在上面流程的第二个点收集用例,就已经告诉了原因。
原因:pycharm相当于是一个master,只接收worker执行完成后返回的测试用例的ids(对应每个测试用例的位置),并不包含完整的测试结果信息
那么想查看分布式运行中测试用例的print打印的话,可以与pytest-html插件搭配使用(这个后续再详细介绍),pytest-html插件也可以通过pip进行安装
pip install pytest-html
在执行用例的时候,加上参数--html html报告路径即可
test_xdist.py示例代码:
import time def test_01(): time.sleep(2) print('用例一执行') def test_02(): time.sleep(2) print('用例二执行') def test_03(): time.sleep(2) assert 2 == 2 print('用例三执行') def test_04(): time.sleep(2) print('用例四执行')
与pytest-html搭配使用,终端命令pytest test_xdist.py -n 4 -sv --html=report.html输出结果:
可以看到pycharm终端并没有打印出测试用例中的输出,只显示了每条测试用例的位置以及测试情况。 但html报告文件却记录了每个worker所有的测试情况,report.html输出定义在当前路径下,可以直接在浏览器中打开
查看Result部分,将每条用例折叠框点击打开,即可看到每条测试用例的打印输出
xdist执行顺序
pytest-xdist默认是无序执行,可以通过--dist来控制执行顺序
--dist=loadscope:按照同一个模块module下的函数和同一个测试类class下的进行分组,将每个测试组发给可以执行的cpu核数(worker),确保同一个组的测试用例在同一个进程中执行。class分组优先于按模块module分组
--dist=loadfile:按照同一个文件名来分组,然后将每个测试组发给可执行的cpu核数(worker),确保同一个组的测试用例在同一个进程执行
可以按照以下测试文件夹目录来验证:
test_case.py:
import pytest #参数化,传入参数 @pytest.mark.parametrize('n',list(range(3))) def test_get_info(n): print('===获取用户个人信息===',n)
test_baidu文件夹下test_case1.py:
import pytest from time import sleep @pytest.mark.parametrize('n',list(range(2))) def test_case1_1(n): sleep(1) print('===baidu 执行测试用例test_case1_1====',n) @pytest.mark.parametrize('n',list(range(2))) def test_case1_2(n): sleep(1) print('===baidu 执行测试用例test_case1_2====',n)
test_baidu文件夹下test_case2.py:
import os def test_01(): print('====1===========') class Test_01: def test_02(self): print('=======2=============')
构建完成后,那么来看下执行顺序--dist=loadscope和--dist=loadfile之间的区别
--dist=loadscope
pycharm终端执行pytest -n auto --dist=loadscope -sv输出结果:
只看有gw进程编号的打印即可。
test_case.py属于一个module模块,且里面没有测试类,不需要再次进行分组。可以看到3组测试用例都被分配到gw0这个进程中进行执行
test_case1.py属于一个module模块,没有定义测试类,所有测试用例是一个分组,在同一个woker(gw1)中执行:
test_case2.py属于一个module模块,但其中定义了一个Test_01测试类,根据class分组优先于module分组,test_case2.py:Test_01测试类下的所有测试用例会再分一个组,也就是在同一个woker(gw3)中执行。如下图:
test_case2.py测试类外的test_01用例则单独分一个组,在woker(gw2)中执行
--dist=loadfile
pycharm终端执行pytest -n auto --dist=loadfile -sv输出结果:
这个更直观了,按照同一个文件名来分组,也就3个文件名:test_case.py、test_case1.py、test_case2.py,依次分配给gw0,gw1,gw3中执行用例,不需要区分测试类啥的,一个文件下的测试用例就是在一个woker中执行的
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 提示词工程——AI应用必不可少的技术
· 地球OL攻略 —— 某应届生求职总结
· 字符编码:从基础到乱码解决
· SpringCloud带你走进微服务的世界