目录

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(分布式执行)相关推荐

  1. python测试框架untest_Python测试框架之unittest和pytest

    目前搜狗商城接口测试框架用的是unittest+HTMLTestRunner,case数有1097条,目前运行一次自动化测试,时长约为30分钟,期望控制在10分钟或者更短的时间内.近期打算重新优化框架 ...

  2. Python测试框架之pytest详解

    目录 前言 1.pytest安装 2.Pytest的setup和teardown函数 3.Pytest配置文件 4 Pytest常用插件 4.1 前置条件: 4.2 Pytest测试报告 5.pyte ...

  3. Python测试框架Pytest的基础入门

    Pytest简介 Pytest is a mature full-featured Python testing tool that helps you write better programs.T ...

  4. 全功能Python测试框架:pytest

    python通用测试框架大多数人用的是unittest+HTMLTestRunner,这段时间看到了pytest文档,发现这个框架和丰富的plugins很好用,所以来学习下pytest. pytest ...

  5. Python测试框架pytest(05)fixture - error和failed、fixture实例化、多个fixture

    Python测试框架pytest系列可以查看下列 Python测试框架pytest(01)简介.安装.快速入门_编程简单学的博客-CSDN博客 Python测试框架pytest(02)PyCharm设 ...

  6. Python测试框架pytest(04)fixture - 测试用例调用fixture、fixture传递测试数据

    Python测试框架pytest系列可以查看下列 Python测试框架pytest(01)简介.安装.快速入门_编程简单学的博客-CSDN博客 Python测试框架pytest(02)PyCharm设 ...

  7. Python测试框架pytest(03)setup和teardown

    Python测试框架pytest系列可以查看下列 Python测试框架pytest(01)简介.安装.快速入门_编程简单学的博客-CSDN博客 ​​​​​​Python测试框架pytest(02)Py ...

  8. 硅谷最爱的测试框架:详解PyTest

    Python中有许多测试框架,但其中最受欢迎的就是PyTest.PyTest是一个强大而灵活的测试框架,它提供了许多先进的功能,可以让你的测试更加简洁.易读. 一.PyTest 简介 PyTest是一 ...

  9. gtest测试框架使用详解_【python】新手小白必看,教你如何使用全功能Python测试框架 - python秋枫...

    大家好,我是在升职加薪道路上越奋斗头发越少的阿茅. 今天来跟想入门还徘徊在门外的小白们聊一聊 1.安装和简单使用 2.配置文件 3.断言 一. 第1步 (安装和简单使用) pytest是一个非常成熟的 ...

最新文章

  1. 茶觉 | “治愈”的白牡丹
  2. win8/Metro开发系七 win8 对常见数据源的解析及处理 如:xml,json,以及html代码
  3. 关于html:form/html:form特性
  4. 嵌入式软件开发工程师的养成之路——从 推挽输出 开始
  5. js控制隐藏或显示table的某一行
  6. 数据结构c语言版实验报告2,数据结构(C语言版) 实验报告 (2)
  7. VS2013 update4+Cocos2d-x 3.7 Win8下安装方法及配置
  8. 尚硅谷大数据技术之 DataX—1)概述
  9. 汇编语言程序设计--基于ARM
  10. 计算机更改后怎么找不到桌面文件,电脑桌面的文件不见了怎么找回
  11. Java笔记——Java 实现金额小写转大写
  12. Inter无线网卡AC 3165无法开启wifi共享的问题
  13. Java中事务的处理全解析
  14. html表单 多行输入文字,如何在HTML中创建多行文本输入(文本区域)?
  15. zk选举机制和分布式一致性原理
  16. 阿里云虚拟主机wordpress伪静态设置Nginx设置
  17. 简单的修改项目中的头像
  18. Android 自动搜索频道,Android自定义收音机搜台控件RadioRulerView
  19. 非静压模型SWASH学习(6)——二维波浪变形模拟算例(Wave transformation over an elliptic shoal on a sloped bottom)
  20. 古风男孩取名:有帝王气质的古风男孩名字

热门文章

  1. 什么是类变量,什么是实例变量,它们之间有什么区别?
  2. 放松心情的好FLASH,闲情逸致“喂鱼”
  3. IDEA 最牛配置33, 从此代码编写爽歪歪
  4. javascrit开发的基本代码结构的
  5. 面对短视频的强攻,直播平台内忧外患如何应变?
  6. 写给想从事数据库方面工作的朋友
  7. 直播卖货系统如何保证连麦音质?来看看回声消除
  8. 开源公告|分布式深度学习训练工具PatrickStar正式开源
  9. 深入理解喷泉码------喷泉码度分布生成流程
  10. Elasticsearch:高级数据类型介绍