如果你还想从头学起Pytest,可以看看这个系列的文章哦!

https://www.cnblogs.com/poloyy/category/1690628.html

背景

  • 使用 pytest-xdist 分布式插件可以加快运行,充分利用机器多核 CPU 的优势
  • 将常用功能放到 fixture,可以提高复用性和维护性
  • 做接口自动化测试的时候,通常我们会将登录接口放到 fixture 里面,并且 scope 会设置为 session,让他全局只运行一次
  • 但是当使用 pytest-xdist 的时候,scope=session 的 fixture 无法保证只运行一次,官方也通报了这一问题

官方描述

  • pytest-xdist 的设计使每个工作进程将执行自己的测试集合并执行所有测试子集,这意味着在不同的测试过程中,要求高级范围的 fixture(如:session)将会被多次执行,这超出了预期,在某些情况下可能是不希望的
  • 尽管 pytest-xdist 没有内置支持来确保  scope=session 的fixture 仅执行一次,但是可以通过使用锁定文件进行进程间通信来实现

前置知识

pytest-xdist 分布式插件使用详细教程可看

https://www.cnblogs.com/poloyy/p/12694861.html

分布式插件原理可看

https://www.cnblogs.com/poloyy/p/12703290.html

fixture 的使用详细教程

https://www.cnblogs.com/poloyy/p/12642602.html

官方文档

https://pypi.org/project/pytest-xdist/

官方解决办法(直接套用就行)

import json

import pytest
from filelock import FileLock @pytest.fixture(scope="session")
def session_data(tmp_path_factory, worker_id):
if worker_id == "master":
# not executing in with multiple workers, just produce the data and let
# pytest's fixture caching do its job
return produce_expensive_data() # get the temp directory shared by all workers
root_tmp_dir = tmp_path_factory.getbasetemp().parent fn = root_tmp_dir / "data.json"
with FileLock(str(fn) + ".lock"):
if fn.is_file():
data = json.loads(fn.read_text())
else:
data = produce_expensive_data()
fn.write_text(json.dumps(data))
return data
  • 若某个 scope = session 的 fixture 需要确保只运行一次的话,可以用上面的方法,直接套用,然后改需要改的部分即可(这个后面详细讲解)
  • 官方原话:这项技术可能并非在每种情况下都适用,但对于许多情况下,它应该是一个起点,在这种情况下,对于 scope = session 的fixture 只执行一次很重要

后续栗子的代码

项目结构

xdist+fixture(文件夹)
│ tmp(存放 allure 数据文件夹)
│ conftest.py
│ test_1.py
│ test_2.py
│ test_3.py
│ __init__.py │

test_1.py 代码

import os

def test_1(test):
print("os 环境变量",os.environ['token'])
print("test1 测试用例", test)

test_2.py 代码

import os

def test_2(test):
print("os 环境变量",os.environ['token'])
print("test2 测试用例", test)

test_3.py 代码

import os

def test_3(test):
print("os 环境变量",os.environ['token'])
print("test3 测试用例", test)

未解决情况下的栗子

conftest.py 代码

import os
import pytest
from random import random @pytest.fixture(scope="session")
def test():
token = str(random())
print("fixture:请求登录接口,获取token", token)
os.environ['token'] = token
return token

运行命令

pytest -n 3 --alluredir=tmp

运行结果

scope=session 的 fixture 很明显执行了三次,三个进程下的三个测试用例得到的数据不一样,明显不会是我们想要的结果

使用官方解决方法的栗子

#!/usr/bin/env python
# -*- coding: utf-8 -*- """
__title__ =
__Time__ = 2021/4/27 11:28
__Author__ = 小菠萝测试笔记
__Blog__ = https://www.cnblogs.com/poloyy/
"""
import json
import os
import pytest
from random import random
from filelock import FileLock @pytest.fixture(scope="session")
def test2(tmp_path_factory, worker_id):
token = str(random())
print("fixture:请求登录接口,获取token", token)
os.environ['token'] = token
return token @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 # 如果测试用例有需要,可以返回对应的数据,比如 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():
# 读取数据,像登录操作的话就是 token 【不可删除、修改】
token = json.loads(fn.read_text())
os.environ['token'] = token
else:
"""
【自定义代码块】
跟上面 if 下面的代码块一样就行
"""
token = str(random())
print("fixture:请求登录接口,获取token", token)
os.environ['token'] = token
# 【不可删除、修改】
fn.write_text(json.dumps(token))
return token

运行命令

pytest -n 3 --alluredir=tmp

运行结果

可以看到 fixture 只执行了一次,不同进程下的测试用例共享一个数据 token

重点

  • 读取缓存文件并不是每个测试用例都会读,它是按照进程来读取的
  • 比如 -n 3 指定三个进程运行,那么有一个进程会执行一次 fixture(随机),另外两个进程会各读一次缓存
  • 假设每个进程有很多个用例,那也只是读一次缓存文件,而不会读多次缓存文件
  • 所以最好要将从缓存文件读出来的数据保存在特定的地方,比如上面代码的 os.environ 可以将数据保存在环境变量中

两个进程跑三个测试用例文件

还是上面栗子的代码

运行命令

pytest -n 2 --alluredir=tmp

运行结果

可以看到 test_3 的测试用例就没有读缓存文件了,每个进程只会读一次缓存文件,记住哦!

Pytest系列(30)- 使用 pytest-xdist 分布式插件,如何保证 scope=session 的 fixture 在多进程运行情况下仍然能只运行一次的更多相关文章

  1. Pytest系列(17)- pytest-xdist分布式测试的原理和流程

    pytest-xdist分布式测试的原理 前言 xdist的分布式类似于一主多从的结构,master机负责下发命令,控制slave机:slave机根据master机的命令执行特定测试任务 在xdist ...

  2. Pytest系列(15)- 多重校验插件之pytest-assume的详细使用

    如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 前言 pytest中可以用pyth ...

  3. Pytest系列(16)- 分布式测试插件之pytest-xdist的详细使用

    如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 前言 平常我们功能测试用例非常多时 ...

  4. Pytest系列(13)- 重复执行用例插件之pytest-repeat的详细使用

    如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 前言 平常在做功能测试的时候,经常 ...

  5. Pytest系列(18)- 超美测试报告插件之allure-pytest的基础使用

    如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 官方介绍 Allure Frame ...

  6. Pytest系列(1) - 快速入门和基础讲解

    如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 前言 目前有两种纯测试的测试框架, ...

  7. Pytest系列(14)- 配置文件pytest.ini的详细使用

    如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 前言 pytest配置文件可以改变 ...

  8. Pytest系列(十三)- 重复执行之pytest-repeat的使用

    写在前面 这个插件,可以帮助我们很好的解决自动化测试过程中的一些偶线性bug难以复现的问题,但前提是,当前自动化脚本是独立的,不依赖任何其他脚本.个人觉得还是失败重运行的一种体现,就和TestNG是一 ...

  9. Pytest(15)pytest分布式执行用例

    前言 平常我们功能测试用例非常多时,比如有1千条用例,假设每个用例执行需要1分钟,如果单个测试人员执行需要1000分钟才能跑完 当项目非常紧急时,会需要协调多个测试资源来把任务分成两部分,于是执行时间 ...

随机推荐

  1. Python Linux 命令行执行脚本输出重定向print到日志文件

    reference: https://unix.stackexchange.com/questions/182537/write-python-stdout-to-file-immediately   ...

  2. Linux fork()一个进程内核态的变化

    [前言]用户态的变化,耳熟能详不在赘述.现在支持读时共享,写时复制. 一.内核态的变化 1.fork一个子进程代码 #include <stdio.h> #include <stdl ...

  3. 在Arch上使用Fcitx5

    目录 卸载Fcitx4 安装Fcitx5 配置 修改环境变量 系统登陆后默认启动Fcitx5输入法 配置主题 最终使用效果 参考文档 我是一个Arch+KDE的用户,所以下面的方法可能不适合所有的Li ...

  4. Linux速通02 命令格式

    命令的格式 # 格式:命令 [选项][参数] * 命令:告诉 Linux操作系统执行什么 * 选项:说明命令运行的方式(可以改变命令的功能).以 "-"字符开始 * 参数:说明命令 ...

  5. Java 基础加强 01

    基础加强·网络编程 和 GUI 网络编程概述 * A:计算机网络 * 是指将地理位置不同的具有独立功能的多台计算机及外部设备,通过通信连接起来 在网路操作系统,网络管理软件和网络通信协议的管理下,实现 ...

  6. python3 获取博彩网站页面下所有域名(批量)

    已有的域名信息 详细实现过程如下 #!/usr/bin/env python # -*- coding:utf-8 -*- import requests from bs4 import Beauti ...

  7. Sentinel高级

    Sentinel高级 sentinel和springCloud整合 减少开发的复杂度,对大部分的主流框架,例如:Web Servlet.Dubbo.Spring Cloud.gRPC.Spring W ...

  8. python学习之基础内容

    python基础内容① 什么是python? -一种计算机语言,计算机语言分为 -高级语言:python.java.Ruby.C#.C++...... -基础语言:C语言.汇编 -计算机可以直接执行基 ...

  9. 如何在 ASP.Net Core 中实现 健康检查

    健康检查 常用于判断一个应用程序能否对 request 请求进行响应,ASP.Net Core 2.2 中引入了 健康检查 中间件用于报告应用程序的健康状态. ASP.Net Core 中的 健康检查 ...

  10. Tomcat详解系列(1) - 如何设计一个简单的web容器

    Tomcat - 如何设计一个简单的web容器 在学习Tomcat前,很多人先入为主的对它的认知是巨复杂的:所以第一步,在学习它之前,要打破这种观念,我们通过学习如何设计一个最基本的web容器来看它需 ...