Python测试框架pytest(22)插件 - pytest-xdist(分布式执行)
目录
1、安装
2、示例
3、原理和流程
4、解决:多进程运行次数
当测试用例非常多的时候,一条条按顺序执行测试用例,是很浪费测试时间的。这时候就可以用到 pytest-xdist,让自动化测试用例可以分布式执行,从而大大节省测试时间。
pytest-xdist 是属于进程级别的并发。
分布式测试用例的设计原则:
(1)独立运行:用例之间是独立的,并且没有依赖关系,还可以完全独立运行。
(2)随机执行:用例执行不强制按顺序执行,支持顺序执行或随机执行。
(3)不影响其他用例:每个用例都能重复运行,运行结果不会影响其他用例。
pytest-xdist 通过一些独特的测试执行模式扩展了 pytest:
(1)测试运行并行化:如果有多个CPU或主机,则可以将它们用于组合的测试运行。这样可以加快开发速度或使用远程计算机的特殊资源。
(2)--looponfail:在子进程中重复运行测试。每次运行之后,pytest 都会等到项目中的文件更改后再运行之前失败的测试。重复此过程,直到所有测试通过,然后再次执行完整运行。
(3)跨平台覆盖:可以指定不同的 Python 解释器或不同的平台,并在所有这些平台上并行运行测试。
1、安装
在命令行中运行以下命令进行安装:
pip install pytest-xdist
或者(使用国内的豆瓣源,数据会定期同步国外官网,速度快。)
pip install pytest-xdist -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
2、示例
创建My_pytest_Demo3项目,并创建如下文件。
如图所示:项目目录结构
根目录下conftest.py文件
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
微信公众号:AllTests软件测试
"""import pytest@pytest.fixture(scope="session")
def login():print("===登录,返回:name,token===")name = "AllTests"token = "123456qwe"yield name, tokenprint("===退出===")
根目录下test_case.py文件
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
微信公众号:AllTests软件测试
"""import pytest
from time import sleep@pytest.mark.parametrize("n", list(range(5)))
def test_get_info(login, n):sleep(1)name, token = loginprint("===获取用户个人信息===", n)print(f"用户名:{name}, token:{token}")
test_baidu包下conftest.py文件
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
微信公众号:AllTests软件测试
"""import pytest@pytest.fixture(scope="module")
def open_baidu(login):name, token = loginprint(f"===用户 {name} 打开baidu===")
test_baidu包下test_case1.py文件
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
微信公众号:AllTests软件测试
"""import pytest
from time import sleep@pytest.mark.parametrize("n", list(range(5)))
def test_case1_1(open_baidu, n):sleep(1)print("===baidu 执行测试用例test_case1_1===", n)@pytest.mark.parametrize("n", list(range(5)))
def test_case1_2(open_baidu, n):sleep(1)print("===baidu 执行测试用例test_case1_2===", n)
test_weibo包下test_case2.py文件
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
微信公众号:AllTests软件测试
"""import pytest
from time import sleep@pytest.mark.parametrize("n", list(range(5)))
def test_case2_no_fixture(login, n):sleep(1)print("===weibo 没有__init__测试用例,执行测试用例test_case2_no_fixture===", login)
test_douyin包下conftest.py文件
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
微信公众号:AllTests软件测试
"""import pytest@pytest.fixture(scope="function")
def open_douyin(login):name, token = loginprint(f"===用户 {name} 打开douyin===")
test_douyin包下test_case3.py文件
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
微信公众号:AllTests软件测试
"""import pytest
from time import sleep@pytest.mark.parametrize("n", list(range(5)))
class TestDouyin:def test_case3_1(self, open_douyin, n):sleep(1)print("===douyin 执行测试用例test_case3_1===", n)def test_case3_2(self, open_douyin, n):sleep(1)print("===douyin 执行测试用例test_case3_2===", n)
1、不使用分布式测试执行测试用例
打开命令行,在该项目根目录下,输入执行命令
pytest -s
执行一条用例大概1s,因为每个用例都加了sleep(1),一共30条用例,总共运行30.16s。
2、使用分布式测试执行测试用例
参数 -n auto:可以自动检测到系统的CPU核数。
使用 auto 等于利用了所有CPU来跑用例,此时CPU占用率会特别高。
打开命令行,在该项目根目录下,输入执行命令
pytest -s -n auto
执行30条用例,只用了4.81s。
3、使用分布式测试执行测试用例(指定多少进程)
打开命令行,在该项目根目录下,输入执行命令
pytest -s -n 5
指定5个进程同时执行30条用例,用时6.99s。
4、pytest-xdist 和 pytest-html 联合使用
打开命令行,在该项目根目录下,输入执行命令
pytest -s -n auto --html=report.html --self-contained-html
执行完成后自动生成的报告
5、按照一定顺序执行
pytest-xdist 默认是无序执行的,可以通过 --dist 参数来控制执行顺序。
--dist=loadscope:将按照同一个模块 module 下的函数和同一个测试类 class 下的方法来分组,然后将每个测试组发给可以执行的 worker,确保同一个组的测试用例在同一个进程中执行。目前无法自定义分组,按类 class 分组优先于按模块 module 分组。
--dist=loadfile:按照同一个文件名来分组,然后将每个测试组发给可以执行的 worker,确保同一个组的测试用例在同一个进程中执行。
6、使 scope=session 的 fixture 在 test session 中仅执行一次
pytest-xdist 是让每个 worker 进程执行属于自己的测试用例集下的所有测试用例。
这意味着在不同进程中,不同的测试用例可能会调用同一个 scope 范围级别较高(例如session)的 fixture,该 fixture 则会被执行多次,这不符合 scope=session 的预期。
尽管 pytest-xdist 没有内置的支持来确保会话范围的 fixture 仅执行一次,但是可以通过使用锁定文件进行进程间通信来实现。
示例:
(1)该示例只需要执行一次login(如只需要执行一次来定义配置选项等)。
(2)当第一次请求这个fixture时,则会利用FileLock仅产生一次fixture数据。需要安装filelock包,安装命令pip install filelock
(3)当其他进程再次请求这个fixture时,则会从文件中读取数据。
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
微信公众号:AllTests软件测试
"""import pytest
from filelock import FileLock@pytest.fixture(scope="session")
def login():print("===登录,返回:name,token===")with FileLock("session.lock"):name = "AllTests"token = "123456qwe"# Web App UI自动化,声明一个driver,再返回# 接口自动化,发起一个登录请求,将token返回yield name, tokenprint("===退出===")
3、原理和流程
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 等事件。
4、解决:多进程运行次数
如何保证 scope=session 的 fixture 在多进程运行情况下仍然只运行一次。
1、创建My_pytest_Demo3_2项目,并创建如下文件。
如图所示:项目目录结构,allure文件夹存放allure测试报告
根目录下conftest.py文件
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
微信公众号:AllTests软件测试
"""import os
import pytest
from random import random@pytest.fixture(scope="session")
def test():token = str(random())print("fixture:请求登录接口,获取token", token)os.environ['token'] = tokenreturn token
根目录下test_case1.py文件
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
微信公众号:AllTests软件测试
"""import osdef test_one(test):print("os 环境变量:", os.environ['token'])print("test_one 测试用例", test)
根目录下test_case2.py文件
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
微信公众号:AllTests软件测试
"""import osdef test_two(test):print("os 环境变量:", os.environ['token'])print("test_two 测试用例", test)
根目录下test_case3.py文件
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
微信公众号:AllTests软件测试
"""import osdef test_three(test):print("os 环境变量:", os.environ['token'])print("test_three 测试用例", test)
2、打开命令行,在该项目根目录下,输入执行命令
pytest -n 3 --alluredir=./allure
allure serve allure
3、运行结果:
scope=session的fixture执行了三次,三个进程下的三个测试用例得到的数据不一样。
一、解决 scope=session 的 fixture 在多进程运行情况下仍然只运行一次
1、修改根目录下conftest.py文件
脚本代码:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
微信公众号:AllTests软件测试
"""
import json
import os
import pytest
from random import random
from filelock import FileLock@pytest.fixture(scope="session")
def test(tmp_path_factory, worker_id):# 如果是单机运行,则运行这里的代码块【不可删除、修改】if worker_id == "master":"""【自定义代码块】这里就写你要本身要做的操作,比如:登录请求、新增数据、清空数据库历史数据等"""token = str(random())print("fixture:请求登录接口,获取token", token)os.environ['token'] = token# 如果测试用例有需要,可以返回对应的数据,比如:tokenreturn token# 如果是分布式运行# 获取所有子节点共享的临时目录,无需修改【不可删除、修改】root_tmp_dir = tmp_path_factory.getbasetemp().parent# 【不可删除、修改】fn = root_tmp_dir / "data.json"# 【不可删除、修改】with FileLock(str(fn) + ".lock"):# 【不可删除、修改】if fn.is_file():# 缓存文件中读取数据,像登录操作的话就是token【不可删除、修改】token = json.loads(fn.read_text())print(f"读取缓存文件,token是:{token}")else:"""【自定义代码块】跟上面if的代码块一样就行"""token = str(random())print("fixture:请求登录接口,获取token", token)# 【不可删除、修改】fn.write_text(json.dumps(token))print(f"首次执行,token是:{token}")# 最好将后续需要保留的数据存在某个地方,比如这里是os的环境变量os.environ['token'] = tokenreturn token
2、打开命令行,在该项目根目录下,输入执行命令
pytest -n 3 --alluredir=./allure
allure serve allure
3、运行结果:
可以看到fixture只执行了一次,不同进程下的测试用例共享一个数据token。
(1)读取缓存文件并不是每个测试用例都会读,它是按照进程来读取的,比如 -n 3 指定三个进程运行,那么有一个进程会执行一次 fixture(随机),另外两个进程会各读一次缓存。
(2)假设每个进程有很多个用例,那也只是读一次缓存文件,而不会读多次缓存文件。所以最好将从缓存文件读出来的数据保存在指定的地方,比如 os.environ 将数据保存在环境变量中。
二、进程少测试用例多的情况下执行
例如:两个进程跑三个测试用例
1、打开命令行,在该项目根目录下,输入执行命令
pytest -n 2 --alluredir=./allure
allure serve allure
2、运行结果:
可以看到test_three的测试用例就没有读缓存文件,每个进程只会读一次缓存文件。
Python测试框架pytest(22)插件 - pytest-xdist(分布式执行)相关推荐
- python测试框架untest_Python测试框架之unittest和pytest
目前搜狗商城接口测试框架用的是unittest+HTMLTestRunner,case数有1097条,目前运行一次自动化测试,时长约为30分钟,期望控制在10分钟或者更短的时间内.近期打算重新优化框架 ...
- Python测试框架之pytest详解
目录 前言 1.pytest安装 2.Pytest的setup和teardown函数 3.Pytest配置文件 4 Pytest常用插件 4.1 前置条件: 4.2 Pytest测试报告 5.pyte ...
- Python测试框架Pytest的基础入门
Pytest简介 Pytest is a mature full-featured Python testing tool that helps you write better programs.T ...
- 全功能Python测试框架:pytest
python通用测试框架大多数人用的是unittest+HTMLTestRunner,这段时间看到了pytest文档,发现这个框架和丰富的plugins很好用,所以来学习下pytest. pytest ...
- Python测试框架pytest(05)fixture - error和failed、fixture实例化、多个fixture
Python测试框架pytest系列可以查看下列 Python测试框架pytest(01)简介.安装.快速入门_编程简单学的博客-CSDN博客 Python测试框架pytest(02)PyCharm设 ...
- Python测试框架pytest(04)fixture - 测试用例调用fixture、fixture传递测试数据
Python测试框架pytest系列可以查看下列 Python测试框架pytest(01)简介.安装.快速入门_编程简单学的博客-CSDN博客 Python测试框架pytest(02)PyCharm设 ...
- Python测试框架pytest(03)setup和teardown
Python测试框架pytest系列可以查看下列 Python测试框架pytest(01)简介.安装.快速入门_编程简单学的博客-CSDN博客 Python测试框架pytest(02)Py ...
- 硅谷最爱的测试框架:详解PyTest
Python中有许多测试框架,但其中最受欢迎的就是PyTest.PyTest是一个强大而灵活的测试框架,它提供了许多先进的功能,可以让你的测试更加简洁.易读. 一.PyTest 简介 PyTest是一 ...
- gtest测试框架使用详解_【python】新手小白必看,教你如何使用全功能Python测试框架 - python秋枫...
大家好,我是在升职加薪道路上越奋斗头发越少的阿茅. 今天来跟想入门还徘徊在门外的小白们聊一聊 1.安装和简单使用 2.配置文件 3.断言 一. 第1步 (安装和简单使用) pytest是一个非常成熟的 ...
最新文章
- 茶觉 | “治愈”的白牡丹
- win8/Metro开发系七 win8 对常见数据源的解析及处理 如:xml,json,以及html代码
- 关于html:form/html:form特性
- 嵌入式软件开发工程师的养成之路——从 推挽输出 开始
- js控制隐藏或显示table的某一行
- 数据结构c语言版实验报告2,数据结构(C语言版) 实验报告 (2)
- VS2013 update4+Cocos2d-x 3.7 Win8下安装方法及配置
- 尚硅谷大数据技术之 DataX—1)概述
- 汇编语言程序设计--基于ARM
- 计算机更改后怎么找不到桌面文件,电脑桌面的文件不见了怎么找回
- Java笔记——Java 实现金额小写转大写
- Inter无线网卡AC 3165无法开启wifi共享的问题
- Java中事务的处理全解析
- html表单 多行输入文字,如何在HTML中创建多行文本输入(文本区域)?
- zk选举机制和分布式一致性原理
- 阿里云虚拟主机wordpress伪静态设置Nginx设置
- 简单的修改项目中的头像
- Android 自动搜索频道,Android自定义收音机搜台控件RadioRulerView
- 非静压模型SWASH学习(6)——二维波浪变形模拟算例(Wave transformation over an elliptic shoal on a sloped bottom)
- 古风男孩取名:有帝王气质的古风男孩名字