pytestx容器化执行引擎

系统架构

前端、后端、pytest均以Docker容器运行服务,单独的容器化执行引擎,项目环境隔离,即用即取,用完即齐,简单,高效。

  • 前端容器:页面交互,请求后端,展示HTML报告

  • 后端容器:接收前端请求,启动任务,构建镜像,触发运行pytest,挂载HTML报告

  • pytest容器:拉取项目代码,指定目录执行,生成HTML报告

说明:构建镜像目前是在宿主机启动后端服务来执行docker命令的,暂未支持Kubernetes编排。宿主机安装了Docker,启动服务后,可以执行docker命令。如果采用容器部署后端,容器里面不包含Docker,无法构建,个人想法是可以借助K8S来编排,当前版本还未实现

系统流程

支持2种运行模式配置:容器和本地。

容器模式:判断是否支持docker,如果支持,构建pytest镜像,在构建时,通过git拉取项目代码,再运行容器,按照指定目录执行pytest,生成测试报告,并将报告文件挂载到后端。如果不支持,降级为本地运行。

本地模式:模拟容器行为,在本地目录拉取代码,执行pytest,生成测试报告。

效果展示

任务管理:

容器模式:

本地模式:

平台大改造

pytestx平台更轻、更薄,移除了用例管理、任务关联用例相关功能代码,只保留真正的任务调度功能,backend的requirements.txt解耦,只保留后端依赖,pytest相关依赖转移到tep-project。

那如何管理用例呢?约定大于配置,我们约定pytest项目已经通过目录维护好了一个稳定的自动化用例集,也就是说需要通过平台任务调度的用例,都统一存放在目录X下,这些用例基本不需要维护,可以每日稳定执行,然后将目录X配置到平台任务信息中,按指定目录执行用例集。对于那些不够稳定的用例,就不能放到目录X下,需要调试好以后再纳入。

为什么不用marker?pytest的marker确实可以给测试用例打标记,也有人是手动建立任务和用例进行映射,这些方式都不如维护一个稳定的自动化用例集方便,在我们公司平台上,也是维护用例集,作为基础用例集。使用pytest项目同理。

核心代码

一键部署

#!/bin/bash
PkgName='backend'Dockerfile='./deploy/Dockerfile.backend'
DockerContext=./echo "Start build image..."
docker build -f $Dockerfile -t $PkgName $DockerContext
if [ $? -eq 0 ]
thenecho "Build docker image success"echo "Start run image..."docker run -p 8000:80 $PkgName
elseecho "Build docker image failed"
fi
FROM python:3.8ENV LANG C.UTF-8
ENV TZ=Asia/ShanghaiRUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezoneWORKDIR /app
COPY ./backend .
RUN pip install -r ./requirements.txt -i \https://pypi.tuna.tsinghua.edu.cn/simple \--default-timeout=3000CMD ["python", "./manage.py", "runserver", "0.0.0.0:80"]

数据库表

更精简,只有project和task两张表,简化平台功能,聚焦任务调度:

需要说明的是,如果多人运行任务,只会存储最后一次执行结果,这个问题不是核心,个人精力有限,不打算在开源项目中开发,更侧重于实现任务调度,供大家参考

执行任务

settings配置任务模式,判断执行不同分支:

def run(self):logger.info("任务开始执行")if settings.TASK_RUN_MODE == TaskRunMode.DOCKER:  # 容器模式try:self.execute_by_docker()except Exception as e:logger.info(e)if e == TaskException.DockerNotSupportedException:logger.info("降级为本地执行")self.execute_by_local()if settings.TASK_RUN_MODE == TaskRunMode.LOCAL:  # 本地模式self.execute_by_local()self.save_task()

容器模式

先根据docker -v命令判断是否支持docker,然后docker build,再docker run

def execute_by_docker(self):logger.info("运行模式:容器")output = subprocess.getoutput("docker -v")logger.info(output)if "not found" in output:raise TaskException.DockerNotSupportedExceptionbuild_args = [f'--build-arg CMD_GIT_CLONE="{self.cmd_git_clone}"',f'--build-arg GIT_NAME="{self.git_name}"',f'--build-arg EXEC_DIR="{self.exec_dir}"',f'--build-arg REPORT_NAME="{self.report_name}"',]cmd = f"docker build {' '.join(build_args)} -f {self.dockerfile_pytest} -t {self.git_name} {BASE_DIR}"logger.info(cmd)output = subprocess.getoutput(cmd)logger.info(output)cmd = f"docker run -v {REPORT_PATH}:/app/{os.path.join(self.exec_dir, 'reports')} {self.git_name}"logger.info(cmd)output = subprocess.getoutput(cmd)logger.info(output)

将项目仓库、执行目录、报告名称信息,通过参数传入Dockerfile.pytest

FROM python:3.8ENV LANG C.UTF-8
ENV TZ=Asia/Shanghai
ARG CMD_GIT_CLONE
ARG GIT_NAME
ARG EXEC_DIR
ARG REPORT_NAMERUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asia/Shanghai' >/etc/timezoneWORKDIR /app
RUN $CMD_GIT_CLONE
RUN pip install -r ./$GIT_NAME/requirements.txt -i \https://pypi.tuna.tsinghua.edu.cn/simple \--default-timeout=3000WORKDIR $EXEC_DIR
ENV HTML_NAME=$REPORT_NAME
CMD ["pytest", "--html=./reports/$HTML_NAME", "--self-contained-html"]

docker run的-v参数将容器报告挂载在后端服务,当报告生成后,后端服务也会生成一份报告文件。再将文件内容返给前端展示:

def report(request, *args, **kwargs):task_id = kwargs["task_id"]task = Task.objects.get(id=task_id)report_path = task.report_pathwith open(os.path.join(REPORT_PATH, report_path), 'r', encoding="utf8") as f:html_content = f.read()return HttpResponse(html_content, content_type='text/html')

测试报告使用的pytest-html,重数据内容,轻外观样式。

本地模式

模拟容器行为,把本地.local目录当做容器,拉代码,执行pytest,生成报告,复制报告到报告文件夹,删除本地目录:

def execute_by_local(self):logger.info("运行模式:本地")os.makedirs(self.local_path, exist_ok=True)os.chdir(self.local_path)cmd_list = [self.cmd_git_clone, self.cmd_pytest]for cmd in cmd_list:logger.info(cmd)output = subprocess.getoutput(cmd)if output:logger.info(output)os.makedirs(REPORT_PATH, exist_ok=True)shutil.copy2(self.project_report_path, REPORT_PATH)shutil.rmtree(LOCAL_PATH)

本地模式,主要用于本地调试,在缺失Docker环境时,也能调试其他功能。

配置

TASK_RUN_MODE = TaskRunMode.DOCKER
LOCAL_PATH = os.path.join(BASE_DIR, ".local")
REPORT_PATH = os.path.join(BASE_DIR, "task", "report")
class TaskRunner:def __init__(self, task_id, run_user_id):self.task_id = task_idself.directory = Task.objects.get(id=task_id).directoryself.project_id = Task.objects.get(id=task_id).project_idself.git_repository = Project.objects.get(id=self.project_id).git_repositoryself.git_branch = Project.objects.get(id=self.project_id).git_branchself.git_name = re.findall(r"^.*/(.*).git", self.git_repository)[0]self.local_path = os.path.join(LOCAL_PATH, str(uuid.uuid1()).replace("-", ""))self.run_user_id = run_user_idself.current_time = time.strftime("%Y-%m-%d-%H-%M-%S", time.localtime(time.time()))self.report_name = f"{str(self.git_name)}-{self.task_id}-{self.run_user_id}-{self.current_time}.html"self.project_report_path = os.path.join(self.local_path, self.git_name, "reports", self.report_name)self.dockerfile_pytest = os.path.join(DEPLOY_PATH, "Dockerfile.pytest")self.exec_dir = os.path.join(self.git_name, self.directory)self.cmd_git_clone = f"git clone -b {self.git_branch} {self.git_repository}"self.cmd_pytest = f"pytest {self.exec_dir} --html={self.project_report_path} --self-contained-html"

tep-project更新

1、整合fixture,功能类放在fixture_function模块,数据类放在其他模块,突出fixture存放数据概念,比如登录接口fixture_login存储用户名密码、数据库fixture_mysql存储连接信息、文件fixture_file_data存储文件路径

2、改造fixture_login,数据类fixture代码更简洁

import pytest
from loguru import logger@pytest.fixture(scope="session")
def login(http, file_data):logger.info("----------------开始登录----------------")response = http("post",url=file_data["domain"] + "/api/users/login",headers={"Content-Type": "application/json"},json={"username": "admin", "password": "qa123456"})assert response.status_code < 400logger.info("----------------登录成功----------------")response = response.json()return {"Content-Type": "application/json", "Authorization": f"Bearer {response['token']}"}@pytest.fixture(scope="session")
def login_xdist(http, tep_context_manager, file_data):"""该login只会在整个运行期间执行一次"""def produce_expensive_data(variable):logger.info("----------------开始登录----------------")response = http("post",url=variable["domain"] + "/api/users/login",headers={"Content-Type": "application/json"},json={"username": "admin", "password": "qa123456"})assert response.status_code < 400logger.info("----------------登录成功----------------")return response.json()response = tep_context_manager(produce_expensive_data, file_data)return {"Authorization": "Bearer " + response["token"]}

3、改造fixture_mysql,支持维护多个连接,并且保持简洁

fixture_function.py

@pytest.fixture(scope="class")
def executor():class Executor:def __init__(self, db):self.db = dbself.cursor = db.cursor()def execute_sql(self, sql):try:self.cursor.execute(sql)self.db.commit()except Exception as e:print(e)self.db.rollback()return self.cursorreturn Executor

fixture_mysql.py

@pytest.fixture(scope="class")
def mysql_execute(executor):db = pymysql.connect(host="host",port=3306,user="root",password="password",database="database")yield executor(db).execute_sqldb.close()@pytest.fixture(scope="class")
def mysql_execute_x(executor):db = pymysql.connect(host="x",port=3306,user="x",password="x",database="x")yield executor(db).execute_sqldb.close()

4、改造fixture_file_data,并添加示例test_file_data.py

import osimport pytestfrom conftest import RESOURCE_PATH@pytest.fixture(scope="session")
def file_data(resource):file_path = os.path.join(RESOURCE_PATH, "demo.yaml")return resource(file_path).get_data()@pytest.fixture(scope="session")
def file_data_json(resource):file_path = os.path.join(RESOURCE_PATH, "demo.json")return resource(file_path).get_data()

5、添加接口复用的示例代码

tests/base就是平台调度使用的稳定自动化用例集。

接口代码复用设计

5条用例:

  1. test_search_sku.py:搜索商品,前置条件:登录

  2. test_add_cart.py:添加购物车,前置条件:登录,搜索商品

  3. test_order.py:下单,前置条件:登录,搜索商品,添加购物车

  4. test_pay.py:支付,前置条件:登录,搜索商品,添加购物车,下单

  5. test_flow.py:完整流程

怎么设计?

  • 登录,每条用例前置条件都依赖,定义为fixture_login,放在fixtures目录下

  • 搜索商品,test_search_sku.py用例本身不需要复用,被前置条件依赖3次,可以复用

①定义为fixture_search_sku放在fixtures❌ 弊端:导致fixtures臃肿

②复制用例文件,允许多份代码,平行展开✅ 好处:高度解耦,不用担心依赖问题

总结,定义为fixture需要具备底层性,足够精炼。对于业务接口用例的前置条件,尽量在用例文件内部处理,保持文件解耦,遵循独立可运行的原则。

复制多份文件?需要修改的话要改多份文件?

是的,但这种情况极少。我能想到的情况:一、框架设计不成熟,动了底层设计,二、接口不稳定,改了公共接口,三、用例设计不合理,不能算是自动化。接口自动化要做好的前提,其实就是框架成熟,接口稳定,用例设计合理,满足这些前提以后,沉淀下来的自动化用例,几乎不需要大批量修改,更多的是要针对每条用例,去修改内部的数据,以满足不同场景的测试需要。也就是说,针对某个用例修改这个用例的数据,是更常见的行为。

如果项目变动实在太大,整个自动化都不能用了,不管是做封装还是平行展开,维护量都非常大,耦合度太高的话,反而还不好改。

跟着pytestx学习接口自动化框架设计,更简单,更快速,更高效

https://github.com/dongfanger/pytestx

https://gitee.com/dongfanger/tep-project

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/110443.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

RHCE——十一、NFS服务器

NFS服务器 一、简介1、NFS背景介绍2、生产应用场景 二、NFS工作原理1、示例图2、流程 三、NFS的使用1、安装2、配置文件3、主配置文件分析3.1 实验1 4、NFS账户映射4.1 实验24.2 实验3 四、autofs自动挂载服务1、产生原因2、安装3、配置文件分析4、实验45、实验5 一、简介 1、…

归一化的作用,sklearn 安装

目录 归一化的作用&#xff1a; 应用场景说明 sklearn 准备工作 sklearn 安装 sklearn 上手 线性回归实战 归一化的作用&#xff1a; 归一化后加快了梯度下降求最优解的速度; 归一化有可能提高精度(如KNN) 应用场景说明 1&#xff09;概率模型不需要归一化&#xff…

FusionAD:用于自动驾驶预测和规划任务的多模态融合

论文背景 自动驾驶&#xff08;AD&#xff09;任务通常分为感知、预测和规划。在传统范式中&#xff0c;AD中的每个学习模块分别使用自己的主干&#xff0c;独立地学习任务。 以前&#xff0c;基于端到端学习的方法通常基于透视视图相机和激光雷达信息直接输出控制命令或轨迹…

基于Spring实现博客项目

访问地址:用户登录 代码获取:基于Spring实现博客项目: Spring项目写博客项目 一.项目开发 1.项目开发阶段 需求评审,需求分析项目设计(接口设计,DB设计等&#xff0c;比较大的需求,需要设计流程图&#xff0c;用例图,UML, model中的字段)开发&#xff0b;自测提测(提交测试…

深入浅出SSD:固态存储核心技术、原理与实战(文末赠书)

名字&#xff1a;阿玥的小东东 学习&#xff1a;Python、C/C 主页链接&#xff1a;阿玥的小东东的博客_CSDN博客-python&&c高级知识,过年必备,C/C知识讲解领域博主 目录 内容简介 作者简介 使用Python做一个计算器 本期赠书 近年来国家大力支持半导体行业&#xff0…

计算机视觉与人工智能在医美人脸皮肤诊断方面的应用

一、人脸皮肤诊断方法 近年来&#xff0c;随着计算机技术和人工智能的不断发展&#xff0c;中医领域开始逐渐探索利用这些先进技术来辅助面诊和诊断。在皮肤望诊方面&#xff0c;也出现了一些现代研究&#xff0c;尝试通过图像分析技术和人工智能算法来客观化地获取皮肤相关的…

循环购商业模式:提升复购率与用户价值的创新策略-微三云门门

亲爱的企业家们&#xff0c;我是微三云门门&#xff01;今天&#xff0c;我将为大家详细介绍一种颠覆性的商业模式&#xff1a;循环购商业模式。这个模式不仅可以帮助企业提升平台的复购率&#xff0c;还能够拉新用户并提升用户的消费率。让我们一起深入了解这个引人注目的商业…

Ubuntu 下安装Qt5.12.12无法输入中文解决方法

Ubuntu 下安装Qt5.12.12无法输入中文解决方法 一&#xff0c;环境&#xff1a; &#xff08;1&#xff09;VMware Workstation 15 Pro &#xff08;2&#xff09;Ubuntu 20.04 &#xff08;3&#xff09;Qt 5.12.12 64bits &#xff08;4&#xff09;Qt Creator 5.0.2 &#…

Hadoop Yarn 核心调优参数

文章目录 测试集群环境说明Yarn 核心配置参数1. 调度器选择2. ResourceManager 调度器处理线程数量设置3. 是否启用节点功能的自动检测设置4. 是否将逻辑处理器当作物理核心处理器5. 设置物理核心到虚拟核心的转换乘数6. 设置 NodeManager 使用的内存量7. 设置 NodeManager 节点…

ant-vue1.78版a-auto-complete表单自动搜索返回列表中的关键字标红

a-auto-complete表单自动搜索返回列表中的关键字标红 通常在做关键字标红的场景&#xff0c;都是后端返回html结构&#xff0c;前端直接渲染实现&#xff0c;但是如果需要前端处理的话&#xff0c;实现也是很简单的&#xff0c;接下来我直接上应用场景吧 应用场景就是通过关键…

GaussDB技术解读系列:高级压缩之OLTP表压缩

8月16日&#xff0c;第14届中国数据库技术大会&#xff08;DTCC2023&#xff09;在北京国际会议中心顺利举行。在GaussDB“五高两易”核心技术&#xff0c;给世界一个更优选择的专场&#xff0c;华为云数据库GaussDB首席架构师冯柯对华为云GaussDB数据库的高级压缩技术进行了详…

centos7搭建apache作为文件站后,其他人无法访问解决办法

在公司内网的一个虚拟机上搭建了httpsd服务&#xff0c;准备作为内部小伙伴们的文件站&#xff0c;但是搭建好之后发现别的小伙伴是无法访问我机器的。 于是寻找一下原因&#xff0c;排查步骤如下&#xff1a; 1.netstat -lnp 和 ps aux 先看下端口和 服务情况 发现均正常 2.…

淘宝商品数据采集(如何快速获取淘宝商品信息),淘宝API接口申请指南

淘宝作为国内的电商平台&#xff0c;拥有海量的商品信息。对于想要进行淘宝商品数据采集的人来说&#xff0c;如何快速获取淘宝商品信息是一个重要的问题。本文将介绍一些快速获取淘宝商品信息的方法。 1. 使用淘宝开放平台PI 淘宝开放平台提供了多种PI接口&#xff0c;可以通…

【微服务部署】01-Kubernetes部署流程

文章目录 部署1. Kubernetes是什么2. Kubernetes的优势3. 环境搭建4. 应用部署 部署 1. Kubernetes是什么 Kubernetes是一个用于自动部署、扩展和管理容器化应用程序的开源系统 2. Kubernetes的优势 自动化容器部署资源管理与容器调度服务注册发现与负载均衡内置配置与秘钥…

【java】【springboot】【idea】springboot项目pom.xml 灰色下划线

解决方案&#xff1a; 这里我们找到了原因&#xff0c;就是因为选择了Ignored Files导致pom.xml文件被设置在maven忽略文件清单中&#xff0c;所以我们将打勾的选项取消&#xff0c;点击Apply,然后点击OK

一文解析:共享WiFi项目到底怎么样呢?

大家都知道&#xff0c;现代社会已经离不开互联网的便利&#xff0c;而WiFi的普及更是提升了人们的生活质量和工作效率。然而&#xff0c;面对庞大的用户群体和不断增长的网络需求&#xff0c;无论人们到哪都是习惯性的连接上wifi。而共享WiFi的出现&#xff0c;正是满足了大众…

Linux基础(一)

1.操作系统概念 人与计算机交流的中介 管理和控制计算机中硬件和软件资源 处于上层应用程序和底层硬件之间的软件平台 2.操作系统组成 内核&#xff1a;直接控制管理硬件 内核直接识别计算机二进制语言 解释器&#xff1a;把c c java python等语言解释成二进制&#xff…

Leetcode每日一题:1267. 统计参与通信的服务器(2023.8.24 C++)

目录 1267. 统计参与通信的服务器 题目描述&#xff1a; 实现代码与解析&#xff1a; 写法一&#xff1a;两次遍历 hash 原理思路&#xff1a; 写法二&#xff1a;三次遍历 原理思路&#xff1a; 1267. 统计参与通信的服务器 题目描述&#xff1a; 这里有一幅服务器分…

215. 数组中的第K个最大元素

题目描述 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 **k** 个最大的元素。 请注意&#xff0c;你需要找的是数组排序后的第 k 个最大的元素&#xff0c;而不是第 k 个不同的元素。 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。 示例 1: 输入: [3,2…

【Axure教程】调用日期选择器并筛选中继器表格

今天教大家在Axure里怎么调用代码调用浏览器的日期选择器并对对中继器表格进行日期区间的筛选。调用浏览器日期选择器的好处是&#xff0c;可以选择真实的日期&#xff0c;包括某年某月某日是星期几&#xff0c;哪个二月是29天……都是真实的&#xff0c;那不同的浏览器日期选择…