pytest运行多个测试用例,pytest并发执行
本文介绍了一个pytest多进程或多线程执行测试的例子,通过示例代码非常详细地介绍了这个例子。对大家的学习或者工作都有一定的参考价值,有需要的朋友可以参考一下。
00-1010序:分布式执行案例原理:项目结构测试脚本的多进程执行案例pytest-xdistpytest-xdist。分布式测试原理:pytest-xdist分布式测试的流程:第一步:master创建workers第二步:工人收集测试项目;第三步:师傅检测工人收集的测试项目。测试用例集第四步:master分发测试用例第五步:worker执行测试用例第六步:测试结束pytest-parallel公共参数配置pytest-parallel和pytest-xdist对比说明3360
目录
实际项目中的用例数量会非常大,成百上千;如果单个进程用于串行执行,将花费大量时间。假设每个用例花费2s,1000个用例将花费2000美元大约3300分钟;再加上耗时,如案例加载、测试前/测试后套件等。导致相对较低的测试执行效率。想象一下,如果我们开发并更改了一段代码,我们需要返回。这个时候,执行自动化用例需要半个多小时或者几个小时,这是我们无法容忍的。为了节省项目测试时间,需要同时并行执行多个测试用例;这是一个分布式场景,可以缩短测试用例的执行时间,提高效率。
前言:
用例是可以独立运行的相互独立的,没有依赖关系,用例执行没有顺序要求,随机顺序可以正常执行;每个用例都可以是重复运行,运行结果不会影响其他用例。
分布式执行用例的原则:
项目结构
# test1/test_1.py
导入时间
def test1_test1():
时间.睡眠(1)
断言1==1, 1==1
def test1_test2():
时间.睡眠(1)
断言1==1, 1==1
类TestDemo1:
定义测试_内部_1(自身):
时间.睡眠(1)
断言1==1, 1==1
类TestDemo2:
def test_inner_2(自身):
时间.睡眠(1)
断言1==1, 1==1
# test1/inner/test_3.py
导入时间
def test3_test1():
时间.睡眠(1)
断言1==1, 1==1
def test3_test2():
时间.睡眠(1)
断言1==1, 1==1
# test2/test_2.py
导入时间
def test2_test1():
时间.睡眠(1)
断言1==1, 1==1
def test2_test2():
时间.睡眠(1)
断言1==1, 1==1
# test2/inner/test_3.py
导入时间
def test4_test1():
时间.睡眠(1)
断言1==1, 1==1
def test4_test2():
时间.睡眠(1)
断言1==1,
1==1"
正常执行:需要8.10s
多进程执行用例之pytest-xdist
多cpu并行执行用例,直接加个-n参数即可,后面num参数就是并行数量,比如num设置为3
pytest -v -n num
参数:
-n auto : 自动侦测系统里的CPU数目
-n num : 指定运行测试的处理器进程数
多进程并行执行:耗时2.66s
大大的缩短了测试用例的执行时间。
pytest-xdist分布式测试的原理:
xdist的分布式类似于一主多从的结构,master负责下发命令,控制slave;slave根据master的命令执行特定测试任务。
在xdist中,主是master,从是workers;xdist会产生一个或多个workers,workers都通过master来控制,每个worker相当于一个
mini版pytest执行器
。master不执行测试任务,只对worker收集到的所有用例进行分发;每个worker负责执行测试用例,然后将执行结果反馈给master;由master统计最终测试结果。
pytest-xdist分布式测试的流程:
第一步:master创建worker
master在
测试会话(test session)
开始前产生一个或多个worker。master和worker之间是通过execnet和网关来通信的。
实际编译执行测试代码的worker可能是本地机器也可能是远程机器。
第二步:workers收集测试项用例
每个worker类似一个迷你型的
pytest执行器
。worker会执行一个完整的
test collection
过程。【收集所有测试用例的过程】然后把测试用例的
ids
返回给master。【ids表示收集到的测试用例路径】master不执行任何测试用例。
注意:分布式测试(pytest-xdist)方式执行测试时不会输出测试用例中的print内容,因为master并不执行测试用例。
第三步:master检测workers收集到的测试用例集
master接收到所有worker收集的测试用例集之后,master会进行一些完整性检查,以确保所有worker都收集到一样的测试用例集(包括顺序)。
如果检查通过,会将测试用例的ids列表转换成简单的索引列表,每个索引对应一个测试用例的在原来测试集中的位置。
这个方案可行的原因是:所有的节点都保存着相同的测试用例集。
并且使用这种方式可以节省带宽,因为master只需要告知workers需要执行的测试用例对应的索引,而不用告知完整的测试用例信息。
第四步:master分发测试用例
有以下四种分发策略:命令行参数--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
只需在最外层conftest中继承
xdist.scheduler.LoadScopeScheduling
并重写_split_scope
方法重写钩子函数
pytest_xdist_make_scheduler
pytest -v -n 4 --dist=loadfile
第五步:worker执行测试用例
workers 重写了
pytest_runtestloop
:pytest的默认实现是循环执行所有在test_session
这个对象里面收集到的测试用例。但是在xdist里, workers实际上是等待master为其发送需要执行的测试用例。
当worker收到测试任务, 就顺序执行
pytest_runtest_protocol
。值得注意的一个细节是:workers 必须始终保持至少一个测试用例在的任务队列里, 以兼容
pytest_runtest_protocol(item, nextitem)
hook的参数要求,为了将nextitem
传给hook。master在worker执行完分配的一组测试后,基于测试执行时长以及每个worker剩余测试用例综合决定是否向这个worker发送更多的测试用例。
worker会在执行最后一个测试项前等待master的更多指令。
如果它收到了更多测试项, 那么就可以安全的执行
pytest_runtest_protocol
,因为这时nextitem
参数已经可以确定。如果它收到一个
shutdown
信号, 那么就将nextitem
参数设为None
, 然后执行pytest_runtest_protocol
第六步:测试结束
当master没有更多执行测试任务时,它会发送一个
shutdown
信号给所有worker。当worker将剩余测试用例执行完后退出进程。
当workers在测试执行结束时,会将结果被发送回master,然后master将结果转发到其他
pytest hooks
比如:pytest_runtest_logstart
、pytest_runtest_logreport
确保整个测试活动进行正常运作。master等待所有worker全部退出并关闭测试会话。
注意:pytest-xdist 是让每个 worker 进程执行属于自己的测试用例集下的所有测试用例。这意味着在不同进程中,不同的测试用例可能会调用同一个 scope 范围级别较高(例如session)的 fixture,该 fixture 则会被执行多次,这不符合 scope=session 的预期。
pytest-xdist 没有内置的支持来确保会话范围的 fixture 仅执行一次,但是可以通过使用锁定文件进行进程间通信来实现;让scope=session 的 fixture 在 test session 中仅执行一次。
示例:需要安装filelock包,安装命令pip install filelock
比如只需要执行一次login(或定义配置选项、初始化数据库连接等)。
当第一次请求这个fixture时,则会利用
FileLock
仅产生一次fixture数据。当其他进程再次请求这个fixture时,则不会重复执行fixture。
import pytest
多线程执行用例之pytest-parallel
用于并行
和并发
测试的 pytest 插件
pip install pytest-parallel
常用参数配置
--workers=n
:多进程运行需要加此参数, n是进程数。默认为1--tests-per-worker=n
:多线程需要添加此参数,n是线程数
如果两个参数都配置了,就是进程并行;每个进程最多n个线程,总线程数:进程数*线程数
【注意】
在windows上进程数永远为1。
需要使用
if name == main :
在命令行窗口运行测试用例会报错
示例:
pytest test.py --workers 3 :3个进程运行
pytest test.py --tests-per-worker 4 :4个线程运行
pytest test.py --workers 2 --tests-per-worker 4 :2个进程并行,且每个进程最多4个线程运行,即总共最多8个线程运行。
import pytest
pytest-parallel与pytest-xdist对比说明:
pytest-parallel 比 pytst-xdist 相对好用,功能支持多;
pytst-xdist 不支持多线程;
pytest-parallel 支持python3.6及以上版本,所以如果想做多进程并发在linux或者mac上做,在Windows上不起作用(Workers=1),如果做多线程linux/mac/windows平台都支持,进程数为workers的值。
pytest-xdist适用场景为:
不是线程安全的
多线程时性能不佳的测试
需要状态隔离
pytest-parallel对于某些用例(如 Selenium)更好:
可以是线程安全的
可以对 http 请求使用非阻塞 IO 来提高性能
简而言之,pytest-xdist
并行性pytest-parallel
是并行性和并发性。
到此这篇关于pytest多进程或多线程执行测试的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持盛行IT软件开发工作室。
郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。