Pytest-BDD实现接口自动化测试,并附全部代码
引言
在之前的文章中简单的介绍了怎么使用Pytest-BDD进行接口测试,可以参考《pytest-bdd 行为驱动自动化测试》。本篇文章主要介绍使用Pytest-BDD实现接口自动化测试。后面的文章会介绍生成测试报告,和流程性接口测试。
feature文件
首先我们先整理好一个接口测试,需要用到的场景、参数等信息,写到feature文件中,以便我们根据各种测试中的行为来进行脚本实现。
demo.feature
Feature: 测试模块Scenario: demo接口测试Given 初始化Given 登录When 调用 "/test" 接口# 这里只配置接口路径,具体的域名和IP,通过config文件配置,以便用了切换环境When 使用 "post" 请求# 目前支持的请求方式 post get put deleteWhen 参数类型 "json"# 目前支持的参数类型 params json form-data form-urlencodedWhen 请求头 "{'Content-type': 'application/json'}"When 参数 "{'id': '1'}"When 校验类型 "包含校验"# 支持的校验类型: 包含校验 相等校验 字段类型校验 字段值校验 状态码校验When 校验文本 "成功"When 校验字段类型为 "int"# 字段类型:int str float list dict boolWhen 校验字段 "loginInfo"When 上传文件 "test.txt"When 退出Then 调用成功
从上面看出,我们要实现的行为有初始化、登录,步骤中需要实现接口地址、请求方式、参数和断言。目前我们需要的接口测试行为已经列出来,下面就是对这些行为的编码实现。
py文件
首先,我们来实现feature文件中的{初始化}。将feature文件中的参数进行初始化。初始化调用api工具的函数。
class ApiTest:def __init__(self):self.api = Noneself.methods = 'get'self.data_type = 'params'self.params = {}self.headers = {}self.test_result_type = Noneself.result_text = Noneself.result_type = Noneself.result_data_type = Noneself.result_key = Noneself.file = Noneself.is_logout = Falsedef call_api(self, test_body):response = api_test(test_body)return response@pytest.fixture
@given('初始化')
def api_tool():return ApiTest()
实现feature文件中的{登录}。登录的目的是拿到接口的授权,所以我们调用登录方法,拿到鉴权后,将鉴权保存到headers中,以便后面进行接口请求时使用。具体实现登录的方法就不展示了,根据自己的项目来实现就可以。
@given('登录')
def login(api_tool):headers = get_token()for key, value in headers.items():api_tool.headers[key] = value
实现feature文件中{调用 “/test” 接口},拿到feature文件中的接口路径,然后赋值给api,后面接口工具会根据环境和接口路径拼接成完整的接口地址。
@when(parsers.parse('调用 "{url}" 接口'))
def api(api_tool, url):api_tool.api = url
实现feature文件中{使用 “post” 请求},拿到feat文件中的请求方法,赋值给methods,后面接口工具使用该请求方法。
@when(parsers.parse('使用 "{methods}" 请求'))
def methods(api_tool, methods):api_tool.methods = methods
实现feature文件中{参数类型 “json”}
@when(parsers.parse('参数类型 "{data_type}"'))
def data_type(api_tool, data_type):api_tool.data_type = data_type
实现feature文件中{请求头 “{‘Content-type’: ‘application/json’}”},因为通过feature文件拿到的参数都是字符串类型,所以要将字符串转成dict,并添加到请求头中。
@when(parsers.parse('请求头 "{headers}"'))
def headers(api_tool, headers):headers_dict = ast.literal_eval(headers)for key, value in headers_dict.items():api_tool.headers[key] = value
实现feature文件中{参数 “{‘id’: ‘1’}”},将字符串类型的参数转成dict。
@when(parsers.parse('参数 "{params}"'))
def params(api_tool, params):api_tool.params = ast.literal_eval(params)
实现feature文件中{校验类型 “包含校验”},根据映射关系,将校验类型数据赋值给test_result_type。
test_result_type_list = {'相等校验': 0,'包含校验': 1,'字段类型校验': 2,'字段值校验': 3,'状态码校验': 4
}@when(parsers.parse('校验类型 "{test_result_type}"'))
def result_test(api_tool, test_result_type):api_tool.test_result_type = test_result_type_list[test_result_type]
实现feature文件中的{校验文本 “成功”}、{校验字段类型为 “int”}、{校验字段 “loginInfo”},这三个加上校验类型,是断言工具需要的,如果是相当校验,会将测试接口返回的response和result_text进行相等校验,如果是包含校验,会校验测试接口的response是否包含result_text,如果是字段类型校验,会校验result_key的类型是否为result_type,如果是字段值校验,会校验测试接口的response返回的result_key的value值,是否等于result_text,如果是状态码校验,会校验response的状态码,是否为result_text。
@when(parsers.parse('校验文本 "{result_text}"'))
def result_text(api_tool, result_text):api_tool.result_text = result_text@when(parsers.parse('校验字段类型为 "{result_type}"'))
def result_type(api_tool, result_type):api_tool.result_type = result_type@when(parsers.parse('校验字段 "{result_key}"'))
def result_key(api_tool, result_key):
实现feature文件中{When 上传文件 “test.txt”},这个步骤只有接口需要上传文件时,才需要写,上传文件放到项目的statics目录下。
@when(parsers.parse('上传文件 "{files}"'))
def file(api_tool, files):api_tool.file = files
实现feature文件中的{退出},is_logout为True时,调用完测试接口会执行退出登录操作。
@when(parsers.parse("退出"))
def logout(api_tool):api_tool.is_logout = True
实现feature文件中{调用成功},将上面步骤保存的参数,拼装成两个dict,test_data为断言工具需要的参数,test_body为接口请求需要的参数,然后调用封装好的接口测试工具和断言工具,进行接口测试。
@then('调用成功')
def asserts(api_tool):test_data = {'test_result': {'text': api_tool.result_text, 'type': api_tool.result_type, 'result_type': api_tool.result_type, 'key': api_tool.result_key}}test_body = {'URL': api_tool.api, 'method': api_tool.methods, 'data_type': api_tool.data_type, 'headers': api_tool.headers, 'params': api_tool.params, 'file': api_tool.file}response = api_tool.call_api(test_body)if api_tool.is_logout:do_logout()if api_tool.test_result_type is not None:assert_tool(response, test_data)
登录和退出的函数就不介绍了,根据自己的项目来实现就可以,主要介绍一下接口测试工具和断言工具。
接口测试工具
接口测试工具中,读取配置文件的参数,放到文章最后,会把测试文件的内容,还有yaml工具的代码附上。
import requests
import os
import json
from common.yaml_tool import read_yamldef api_test(test_body):config = read_yaml('config.yaml')config_env = config['env']data_type = test_body['data_type'].lower()url = config_env + test_body['URL']method = test_body['method'].lower()data = test_body['params']headers = test_body['headers']if headers is None:headers = {}file = test_body['file']if data_type.lower() == 'params':response = params(url, method, data, headers)elif data_type == 'json':response = json(url, method, data, headers)elif data_type == 'form-data':response = formdata(url, method, data, headers, file)elif data_type == 'form-urlencoded':response = urlencoded(url, method, data, headers)else:return {'code': '5000', 'msg': '测试脚本暂时不支持的参数类型'}# print('接口返回内容:' + response.text)return responsedef params(url, method, data, headers):try:if method == 'post':response = requests.post(url, params=data, headers=headers)elif method == 'put':response = requests.put(url, params=data, headers=headers)elif method == 'get':response = requests.get(url, params=data, headers=headers)elif method == 'delete':response = requests.delete(url, params=data, headers=headers)else:response = {'code': '5000', 'msg': '测试脚本暂时不支持的请求方法'}response.encoding = 'utf-8'return responseexcept Exception as e:return {"code": "5000", "msg": str(e)}def json(url, method, data, headers):try:if method == 'post':response = requests.post(url, data=data, headers=headers)elif method == 'put':response = requests.put(url, data=data, headers=headers)elif method == 'get':response = requests.get(url, data=data, headers=headers)elif method == 'delete':response = requests.delete(url, data=data, headers=headers)else:response = {'code': '5000', 'msg': '测试脚本暂时不支持的请求方法'.encode('utf-8')}return responseexcept Exception as e:return json.dumps({'code': '5000', 'msg': '测试脚本报错:' + str(e)})def urlencoded(url, method, data, headers):try:if method == 'post':response = requests.post(url, data=data, headers=headers)elif method == 'put':response = requests.put(url, data=data, headers=headers)elif method == 'get':response = requests.get(url, data=data, headers=headers)elif method == 'delete':response = requests.delete(url, data=data, headers=headers)else:response = {'code': '5000', 'msg': '测试脚本暂时不支持的请求方法'.encode('utf-8')}return responseexcept Exception as e:return {'code': '5000', 'msg': '测试脚本报错:' + str(e)}def formdata(url, method, data, headers, file):if file:file_name = filename = os.path.splitext(file_name)[-1]if name == '.doc':file_type = 'application/msword'elif name == '.docx':file_type = 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'elif name == '.pdf':file_type = 'application/pdf'elif name == '.jpg' or name == 'jpeg':file_type = 'image/jpeg'elif name == '.png':file_type = 'image/png'elif name == '.ppt':file_type = 'application/vnd.ms-powerpoint'elif name == '.xls':file_type = 'application/vnd.ms-excel'elif name == '.xlsx':file_type = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'elif name == '.txt':file_type = 'text/plain'path = os.path.dirname(os.path.dirname(__file__))file = [('file', ('Test'+name, open(path + '/statics/' + file_name, 'rb'), file_type))]try:if method == 'post':response = requests.post(url, data=data, files=file, headers=headers)elif method == 'put':response = requests.put(url, data=data, files=file, headers=headers)elif method == 'get':response = requests.get(url, data=data, files=file, headers=headers)elif method == 'delete':response = requests.delete(url, data=data, files=file, headers=headers)else:response = {'code': '5000', 'msg': '测试脚本暂时不支持的请求方法'.encode('utf-8')}return responseexcept Exception as e:return {'code': '5000', 'msg': '测试脚本报错:' + str(e)}
断言工具
import json
from requests import Responsedef assert_tool(response, test_result):response_body = Noneif type(response) is dict:response_body = responseelse:response_body = response.texttext = test_result['test_result']['text']types = test_result['test_result']['type']type_map = {'int': int,'float': float,'str': str,'list': list,'dict': dict,'bool': bool,}if types == 0:assert text == str(response_body)elif types == 1:assert text in str(response_body)elif types == 2:result_type = test_result['test_result']['result_type']key_str = test_result['test_result']['key']assert isinstance(analysis_dict(json.loads(response_body), key_str), type_map.get(result_type))elif types == 3:key_str = test_result['test_result']['key']assert text == str(analysis_dict(json.loads(response_body), key_str))elif types == 4:if type(response) is Response:status = response.status_codeassert text == statuselse:assert text == str(response_body)def analysis_dict(response: dict, key_str):if key_str in response:return response[key_str]for key, value in response.items():if isinstance(value, dict):result = analysis_dict(value, key_str)if result is not None:return resultelif isinstance(value, list):for item in value:if isinstance(item, dict):result = analysis_dict(item, key_str)if result is not None:return resultreturn None
本接口自动化测试工具,基本实现了接口测试和断言。后面会加上测试报告,还有流程性接口测试(如接口2的参数,需要从接口1中获取)
配置文件
env: http://yourdomin.com
login:URL: /loginheaders:content-type: application/jsonparams:username: usernamepassword: password
logout:URL: /logoutheaders:content-type: application/json
yam文件工具
import yamldef read_yaml(yaml_file):with open(yaml_file, 'r', encoding='utf-8') as file:test_data = yaml.safe_load(file)return test_datadef wirte_config(config, config_file):with open(config_file, 'w', encoding='utf-8') as file:yaml.dump(config, file, default_flow_style=False)
python文件
实现feature文件的整体python文件
import pytest
from pytest_bdd import scenarios, given, when, then, parsersfrom common.api_tool import api_test
from common.token_api import get_token, get_cms_token, do_logout
from common.assert_tool import assert_tool
import ast
import ostest_result_type_list = {'相等校验': 0,'包含校验': 1,'字段类型校验': 2,'字段值校验': 3,'状态码校验': 4
}class ApiTest:def __init__(self):self.api = Noneself.methods = 'get'self.data_type = 'params'self.params = {}self.headers = {}self.test_result_type = Noneself.result_text = Noneself.result_type = Noneself.result_data_type = Noneself.result_key = Noneself.file = Noneself.is_logout = Falsedef call_api(self, test_body):response = api_test(test_body)return responsefor root, dirs, files in os.walk('case/'):for case in files:scenarios(root + case)@pytest.fixture
@given('初始化')
def api_tool():return ApiTest()@given('登录')
def login(api_tool):headers = get_token()for key, value in headers.items():api_tool.headers[key] = value@when(parsers.parse('调用 "{url}" 接口'))
def api(api_tool, url):api_tool.api = url@when(parsers.parse('使用 "{methods}" 请求'))
def methods(api_tool, methods):api_tool.methods = methods@when(parsers.parse('参数类型 "{data_type}"'))
def data_type(api_tool, data_type):api_tool.data_type = data_type@when(parsers.parse('请求头 "{headers}"'))
def headers(api_tool, headers):headers_dict = ast.literal_eval(headers)for key, value in headers_dict.items():api_tool.headers[key] = value@when(parsers.parse('参数 "{params}"'))
def params(api_tool, params):api_tool.params = ast.literal_eval(params)@when(parsers.parse('校验类型 "{test_result_type}"'))
def result_test(api_tool, test_result_type):api_tool.test_result_type = test_result_type_list[test_result_type]@when(parsers.parse('校验文本 "{result_text}"'))
def result_text(api_tool, result_text):api_tool.result_text = result_text@when(parsers.parse('校验字段类型为 "{result_type}"'))
def result_type(api_tool, result_type):api_tool.result_type = result_type@when(parsers.parse('校验字段 "{result_key}"'))
def result_key(api_tool, result_key):api_tool.result_key = result_key@when(parsers.parse('上传文件 "{files}"'))
def file(api_tool, files):api_tool.file = files@when(parsers.parse("退出"))
def logout(api_tool):api_tool.is_logout = True@then('调用成功')
def asserts(api_tool):test_data = {'test_result': {'text': api_tool.result_text, 'type': api_tool.result_type, 'result_type': api_tool.result_type, 'key': api_tool.result_key}}test_body = {'URL': api_tool.api, 'method': api_tool.methods, 'data_type': api_tool.data_type, 'headers': api_tool.headers, 'params': api_tool.params, 'file': api_tool.file}response = api_tool.call_api(test_body)if api_tool.is_logout:do_logout()if api_tool.test_result_type is not None:assert_tool(response, test_data)相关文章:
Pytest-BDD实现接口自动化测试,并附全部代码
引言 在之前的文章中简单的介绍了怎么使用Pytest-BDD进行接口测试,可以参考《pytest-bdd 行为驱动自动化测试》。本篇文章主要介绍使用Pytest-BDD实现接口自动化测试。后面的文章会介绍生成测试报告,和流程性接口测试。 feature文件 首先我们先整理好…...
Sqli-labs-master靶场--布尔盲注
目录 1、布尔盲注 2、布尔盲注的流程(以靶场less-8为例) 2.1输入id尝试是否存在注入点 2.1.1通过以上尝试,联想到可能是布尔盲注 2.2猜测数据库长度 2.3获取数据库名 2.3.1python脚本获取 代码: 获取结果为: …...
【QGroundControl二次开发】十. QT添加GStreamer视频播放同时保存
上一章介绍使用QT播放GStreamer视频流 【QGroundControl二次开发】八. QT实现播放gstreamer视频。 这章介绍如何在原有基础上保存为视频,同时保存为一个个规定大小的小视频。 先展示代码: #include <QApplication> #include <QWidget> #include <QtConcurrent…...
double类型 精度丢失的问题
前言 精度丢失的问题是在其他计算机语言中也都会出现,float和double类型的数据在执行二进制浮点运算的时候,并没有提供完全精确的结果。产生误差不在于数的大小,而是因为数的精度。 一、double进行运算时,经常出现精度丢失 0.10.2使用计算…...
C++ 重要特性探究
shared_from_this 使用分析 场景 类的成员函数需要获取指向自身的shared_ptr的时候类成员函数传递shared_ptr给其他函数或者对象的时候,目的是为了管理对象生命周期使用方法 首先类必须继承 std::enable_shared_from_this<T>必须使用 shared_from_this 获取指…...
c++_游戏_狼人杀
思路主要包括以下几个部分: 角色分配:代码中通过随机数的方式给狼人、平民、预言家和法师等角色进行分配,保证每个角色的数量和身份的随机性。 游戏进行:根据狼人、平民、预言家和法师等角色的身份,游戏进行了夜晚和白…...
MySQL——数据类型、索引的建立、数据的约束
文章目录 数据类型索引的建立普通索引唯一索引使用ALTER 命令添加和删除索引使用ALTER 命令添加和删除主键显示索引信息 数据的约束非空约束:not null,值不能为null唯一约束:unique,值不能重复主键约束:primary key外键…...
常见框架漏洞详解③!!
Apache Apache 是世界使⽤排名第⼀的 Web 服务器软件。它可以运⾏在⼏乎所有⼴泛使⽤的计算 机平台上,由于其跨平台和安全性被⼴泛使⽤,是最流⾏的 Web 服务器端软件之⼀。 apache⽬录结构: bin:存放常⽤命令⼯具,如h…...
大数据基础知识
大数据(Big Data)是指无法用传统数据处理工具和技术有效处理的大规模、复杂的数据集。大数据技术通过对这些数据进行存储、处理和分析,从中提取有价值的信息和见解。 1. 大数据的特点 大数据通常具有以下四个主要特点,被称为“4…...
SQL Server 的透明数据加密
透明数据加密是SQL Server数据库安全众多特性中的一个,本文只针对透明数据加密。 在此测试之前,已经按照文档如何快速获得一个测试用SQL Server企业版创建了一个SQL Server 2019,并按照文档为SQL Server安装示例数据库AdventureWorks安装了…...
Windows图形界面(GUI)-MFC-C/C++ - 列表视图(List Control) - CListCtrl
公开视频 -> 链接点击跳转公开课程博客首页 -> 链接点击跳转博客主页 目录 列表视图(List Control) - CListCtrl 创建列表视图 设置列表视图属性 成员函数 注意事项 示例代码 列表视图(List Control) - CListCtrl 创建列表视图 在对话框编辑器中ÿ…...
一机两用的简单介绍
电子政务外网终端使用过程的风险与挑战 1、终端防护弱,失陷风险大 政务外网终端具备访问互联网能力,造成政务外网终端极易感染僵木蠕病毒,破坏正常办公 政务外网终端易被攻击失陷,成为从互联网攻击政务外网的跳板机 2、VPN漏洞…...
uniapp离线打包热更新失败-AndroidStudio离线打包apk后无法下载打开-热更新失败-plus.runtime.install失败
效果图 仅安卓 前言 1.plus.runtime.install一直fail(20240808), uni.openDocument可以打开本地apk文件 2.权限问题需小心 跑通前提 1.先确定apk地址有效,浏览器中手动下载可安装 2.确保已添加离线打包AndroidStudio的“android.permission.INSTALL_PACKAGES”权…...
深植根基、蓬勃向上 | openKylin 2.0正式发布!
2024年8月8日,openKylin 2.0版本正式发布!该版本默认搭载Linux 6.6 LTS内核,完成180操作系统核心组件自主选型升级,深度融合AI技术,上线麒麟AI助手等实用AI功能,并为用户带来包括开明软件包格式、不可变系统…...
【Material-UI】按钮组:尺寸与颜色详解
文章目录 一、按钮组概述1. 组件介绍2. 基本用法 二、按钮组的尺寸(Sizes)1. 小尺寸(Small)2. 中等尺寸(Medium)3. 大尺寸(Large) 三、按钮组的颜色(Colors)1…...
app抓包 burp配置
证书导出 模拟器安装证书 点击安装证书 将证书直接拖进来就行 配置代理 打开浏览器抓包...
图像与像素:利用ImageJ分析荧光显微镜图像|QuPath基础教程1|24-08-08
内容概要 数字图像由像素组成,每个像素具有一个数值,通常与检测到的光线相关。相同的像素值可以通过不同的方式进行显示。在科学图像处理中,可以通过修改查找表来独立于像素值改变图像外观。 一、引言 图像由其最小的组成单位——像素构成。…...
Prompt Fuzzer:用于增强 GenAI 应用程序的开源工具
Prompt Fuzzer 是一个开源工具,可以评估GenAI应用程序的系统提示针对基于动态 LLM 的威胁的安全性。 Prompt Fuzzer 功能: 1. 模拟十几种类型的 GenAI 攻击。 2. 该工具会根据系统提示自动进行情境化,针对与 GenAI 应用程序相关的特定主题或行…...
Vision Pro使用GLFT 加载模型shader错误解决办法
Glft shader在vision pro上加载错误 前言相关背景解决办法 参考文章 前言 之前在Vision Pro上尝试加载Glb文件,但是加载完成后发现加载出来的Glb文件材质不正确。材质是黑色的。因此整理一下解决方案。 相关背景 使用Unity开发,Glb的加载插件为gltf F…...
Netty技术全解析:MessageToMessageCodec类深度解析
❃博主首页 : 「码到三十五」 ,同名公众号 :「码到三十五」,wx号 : 「liwu0213」 ☠博主专栏 : <mysql高手> <elasticsearch高手> <源码解读> <java核心> <面试攻关> ♝博主的话 :…...
基于uniapp+WebSocket实现聊天对话、消息监听、消息推送、聊天室等功能,多端兼容
基于 UniApp + WebSocket实现多端兼容的实时通讯系统,涵盖WebSocket连接建立、消息收发机制、多端兼容性配置、消息实时监听等功能,适配微信小程序、H5、Android、iOS等终端 目录 技术选型分析WebSocket协议优势UniApp跨平台特性WebSocket 基础实现连接管理消息收发连接…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
Linux云原生安全:零信任架构与机密计算
Linux云原生安全:零信任架构与机密计算 构建坚不可摧的云原生防御体系 引言:云原生安全的范式革命 随着云原生技术的普及,安全边界正在从传统的网络边界向工作负载内部转移。Gartner预测,到2025年,零信任架构将成为超…...
JUC笔记(上)-复习 涉及死锁 volatile synchronized CAS 原子操作
一、上下文切换 即使单核CPU也可以进行多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制。时间片非常短,所以CPU会不断地切换线程执行,从而让我们感觉多个线程是同时执行的。时间片一般是十几毫秒(ms)。通过时间片分配算法执行。…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
html-<abbr> 缩写或首字母缩略词
定义与作用 <abbr> 标签用于表示缩写或首字母缩略词,它可以帮助用户更好地理解缩写的含义,尤其是对于那些不熟悉该缩写的用户。 title 属性的内容提供了缩写的详细说明。当用户将鼠标悬停在缩写上时,会显示一个提示框。 示例&#x…...
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习)
Aspose.PDF 限制绕过方案:Java 字节码技术实战分享(仅供学习) 一、Aspose.PDF 简介二、说明(⚠️仅供学习与研究使用)三、技术流程总览四、准备工作1. 下载 Jar 包2. Maven 项目依赖配置 五、字节码修改实现代码&#…...
在QWebEngineView上实现鼠标、触摸等事件捕获的解决方案
这个问题我看其他博主也写了,要么要会员、要么写的乱七八糟。这里我整理一下,把问题说清楚并且给出代码,拿去用就行,照着葫芦画瓢。 问题 在继承QWebEngineView后,重写mousePressEvent或event函数无法捕获鼠标按下事…...
嵌入式学习笔记DAY33(网络编程——TCP)
一、网络架构 C/S (client/server 客户端/服务器):由客户端和服务器端两个部分组成。客户端通常是用户使用的应用程序,负责提供用户界面和交互逻辑 ,接收用户输入,向服务器发送请求,并展示服务…...
Python+ZeroMQ实战:智能车辆状态监控与模拟模式自动切换
目录 关键点 技术实现1 技术实现2 摘要: 本文将介绍如何利用Python和ZeroMQ消息队列构建一个智能车辆状态监控系统。系统能够根据时间策略自动切换驾驶模式(自动驾驶、人工驾驶、远程驾驶、主动安全),并通过实时消息推送更新车…...
