分布式执行用例原则是什么,pytest-xdist分布式测试怎么做
Admin 2022-08-03 群英技术资讯 451 次浏览
单进程串行
执行的话会非常耗费时间。假设每条用例耗时2s,1000条就需要2000s $\approx$ 33min;还要加上用例加载、测试前/后置套件等耗时;导致测试执行效率会相对低。并行执行
;这就是一种分布式场景
来缩短测试用例的执行时间,提高效率。# test1/test_1.py import time def test1_test1(): time.sleep(1) assert 1 == 1, "1==1" def test1_test2(): time.sleep(1) assert 1 == 1, "1==1" class TestDemo1: def test_inner_1(self): time.sleep(1) assert 1 == 1, "1==1" class TestDemo2: def test_inner_2(self): time.sleep(1) assert 1 == 1, "1==1" # test1/inner/test_3.py import time def test3_test1(): time.sleep(1) assert 1 == 1, "1==1" def test3_test2(): time.sleep(1) assert 1 == 1, "1==1" # test2/test_2.py import time def test2_test1(): time.sleep(1) assert 1 == 1, "1==1" def test2_test2(): time.sleep(1) assert 1 == 1, "1==1" # test2/inner/test_3.py import time def test4_test1(): time.sleep(1) assert 1 == 1, "1==1" def test4_test2(): time.sleep(1) assert 1 == 1, "1==1"
正常执行:需要8.10s
多cpu并行执行用例,直接加个-n参数即可,后面num参数就是并行数量,比如num设置为3
pytest -v -n num
参数:
多进程并行执行:耗时2.66s
大大的缩短了测试用例的执行时间。
xdist的分布式类似于一主多从的结构,master负责下发命令,控制slave;slave根据master的命令执行特定测试任务。
在xdist中,主是master,从是workers;xdist会产生一个或多个workers,workers都通过master来控制,每个worker相当于一个mini版pytest执行器
。
master不执行测试任务,只对worker收集到的所有用例进行分发;每个worker负责执行测试用例,然后将执行结果反馈给master;由master统计最终测试结果。
master在测试会话(test session)
开始前产生一个或多个worker。
master和worker之间是通过execnet和网关来通信的。
实际编译执行测试代码的worker可能是本地机器也可能是远程机器。
每个worker类似一个迷你型的pytest执行器
。
worker会执行一个完整的test collection
过程。【收集所有测试用例的过程】
然后把测试用例的ids
返回给master。【ids表示收集到的测试用例路径】
master不执行任何测试用例。
注意:分布式测试(pytest-xdist)方式执行测试时不会输出测试用例中的print内容,因为master并不执行测试用例。
master接收到所有worker收集的测试用例集之后,master会进行一些完整性检查,以确保所有worker都收集到一样的测试用例集(包括顺序)。
如果检查通过,会将测试用例的ids列表转换成简单的索引列表,每个索引对应一个测试用例的在原来测试集中的位置。
这个方案可行的原因是:所有的节点都保存着相同的测试用例集。
并且使用这种方式可以节省带宽,因为master只需要告知workers需要执行的测试用例对应的索引,而不用告知完整的测试用例信息。
有以下四种分发策略:命令行参数 --dist=mode选项
(默认load
)
each:master将完整的测试索引列表分发到每个worker,即每个worker都会执行一遍所有的用例。
load:master将大约$\frac{1}{n}$的测试用例以轮询的方式分发到各个worker,剩余的测试用例则会等待worker执行完测试用例以后再分发;每个用例只会被其中一个worker执行一次。
loadfile:master分发用例的策略为按ids
中的文件名(test_xx.py/xx_test.py)进行分发,即同一个测试文件中的测试用例只会分发给其中一个worker;具有一定的隔离性。
loadscope:master分发用例对策略为按作用域进行分发,同一个模块下的测试函数或某个测试类中的测试函数会分发给同一个worker来执行;即py文件中无测试类的话(只有测试function)将该模块分发给同一个worker执行,如果有测试类则会将该文件中的测试类只会分发给同一个worker执行,多个类可能分发给多个worker;目前无法自定义分组,按类 class 分组优先于按模块 module 分组。
注意:可以使用pytest_xdist_make_scheduler
这个hook来实现自定义测试分发逻辑。
如:想按目录级别来分发测试用例:
from xdist.scheduler import LoadScopeScheduling class CustomizeScheduler(LoadScopeScheduling): def _split_scope(self, nodeid): return nodeid.split("/", 1)[0] def pytest_xdist_make_scheduler(config, log): return CustomizeScheduler(config, log)
xdist.scheduler.LoadScopeScheduling
并重写_split_scope
方法pytest_xdist_make_scheduler
pytest -v -n 4 --dist=loadfile
pytest_runtestloop
:pytest的默认实现是循环执行所有在test_session
这个对象里面收集到的测试用例。pytest_runtest_protocol
。pytest_runtest_protocol(item, nextitem)
hook的参数要求,为了将nextitem
传给hook。pytest_runtest_protocol
,因为这时nextitem
参数已经可以确定。shutdown
信号, 那么就将nextitem
参数设为None
, 然后执行 pytest_runtest_protocol
shutdown
信号给所有worker。pytest hooks
比如:pytest_runtest_logstart
、pytest_runtest_logreport
确保整个测试活动进行正常运作。注意:pytest-xdist 是让每个 worker 进程执行属于自己的测试用例集下的所有测试用例。这意味着在不同进程中,不同的测试用例可能会调用同一个 scope 范围级别较高(例如session)的 fixture,该 fixture 则会被执行多次,这不符合 scope=session 的预期。
pytest-xdist 没有内置的支持来确保会话范围的 fixture 仅执行一次,但是可以通过使用锁定文件进行进程间通信来实现;让scope=session 的 fixture 在 test session 中仅执行一次。
示例:需要安装 filelock 包,安装命令pip install filelock
FileLock
仅产生一次fixture数据。import pytest from filelock import FileLock @pytest.fixture(scope="session") def login(tmp_path_factory, worker_id): # 代表是单机运行 if worker_id == "master": token = str(random()) print("fixture:请求登录接口,获取token", token) os.environ['token'] = token return token # 分布式运行 # 获取所有子节点共享的临时目录,无需修改【不可删除、修改】 root_tmp_dir = tmp_path_factory.getbasetemp().parent fn = root_tmp_dir / "data.json" with FileLock(str(fn) + ".lock"): if fn.is_file(): # 代表已经有进程执行过该fixture token = json.loads(fn.read_text()) else: # 代表该fixture第一次被执行 token = str(random()) fn.write_text(json.dumps(token)) # 最好将后续需要保留的数据存在某个地方,比如这里是os的环境变量 os.environ['token'] = token return token
用于并行
和并发
测试的 pytest 插件
pip install pytest-parallel
--workers=n
:多进程运行需要加此参数, n是进程数。默认为1
--tests-per-worker=n
:多线程需要添加此参数,n是线程数
如果两个参数都配置了,就是进程并行;每个进程最多n个线程,总线程数:进程数*线程数
【注意】
在windows上进程数永远为1。
需要使用 if name == “main” :
在命令行窗口运行测试用例会报错
示例:
import pytest def test_01(): print('测试用例1操作') def test_02(): print('测试用例2操作') def test_03(): print('测试用例3操作') def test_04(): print('测试用例4操作') def test_05(): print('测试用例5操作') def test_06(): print('测试用例6操作') def test_07(): print('测试用例7操作') def test_08(): print('测试用例8操作') if __name__ == "__main__": pytest.main(["-s", "test_b.py", '--workers=2', '--tests-per-worker=4'])
简而言之,pytest-xdist
并行性pytest-parallel
是并行性和并发性。
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:mmqy2019@163.com进行举报,并提供相关证据,查实之后,将立刻删除涉嫌侵权内容。
猜你喜欢
turtle库是python的基础绘图库,经常被用来给孩子们介绍编程知识的方法库,是标准库之一,利用turtle可以制作很多复杂的绘图。本文将为大家介绍通过turtle库绘制的一些有趣的图画,感兴趣的小伙伴可以学习一下
这篇文章主要介绍了学好python基本数据类型,学习python基本数据类型我们需要了解基本数据类型有数字int、布尔值bool、字符串str、列表list、元组tuple、字典dict等,其中包括他们的基本用法和其常用的方法,下面来看看文章的具体介绍吧
如何计算python代码耗时是多少?一些Python新手不太清楚要怎样统计python代码耗时,文本就给大家介绍几种方法,时间戳相减、装饰器、timeit模块、重复调用 timeit()和cProfile性能分析工具。下面我们一起来具体看看吧。
os.walk方法是python中帮助我们高效管理文件、目录的工具,在深度学习中数据整理应用的很频繁,如数据集的名称格式化、将数据集的按一定比例划分训练集train_set、测试集test_set。
在Python2中,除法的取值结果取整数>>>7 32即一个整数(无小数部分的数)被另外一个整数除,计算结果的小数部分被截除了,只留下了整数部分
成为群英会员,开启智能安全云计算之旅
立即注册Copyright © QY Network Company Ltd. All Rights Reserved. 2003-2020 群英 版权所有
增值电信经营许可证 : B1.B2-20140078 粤ICP备09006778号 域名注册商资质 粤 D3.1-20240008