news 2026/7/5 18:45:32

乐购商城接口自动化项目架构介绍

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
乐购商城接口自动化项目架构介绍

一、项目介绍

基于Python+pytest+sqlalchemy+requests+allure+jsonpath+yaml+Jenkins+Linux

该项目是一个在线购物的商城网站,包括用户注册,登录,下单,上架/下架商品,下单支付等相关功能。

二、项目结构说明

pythonproject-root/ # 项目根目录 ├─ base/ # 基础类封装(核心功能)、测试用例工具 │ └─ new_testcase_tools.py # 测试用例工具类 ├─ common/ # 公共方法封装 ├─ conf/ # 存放全局配置文件目录 │ ├─ config.ini # 环境配置文件 ├─ data/ # 存放测试数据路径 ├─ logs/ # 存放测试日志目录 │ └─ test.20251009.log # 按日期命名的日志文件 ├─ report/ # 测试报告生成目录,目前支持生成两种形式的报告 │ ├─ allureReport # Allure交互式报告 │ └─ tmreport # TMReport表格报告 ├─ testcase/ #存放测试用例文件目录 ├─ venv/ # 本框架使用的虚拟环境 ├─ conftest.py # 全局操作(固定名称) ├─ environment.xml # allure测试报告总览-环境显示内容 ├─ extract.yaml # 接口依赖参数存放文件 ├─ pytest.ini # pytest框架规范约束 ├─ requirements.txt # 本框架所使用的到的第三方库 └─ run.py # 主程序入口

三、核心代码

1. 程序入口 run.py

代码如下:

import shutil import pytest import os import webbrowser from conf.setting import REPORT_TYPE if __name__ == '__main__': if REPORT_TYPE == 'allure': pytest.main( ['-s', '-v', '--alluredir=./report/temp', './testcase', '--clean-alluredir', '--junitxml=./report/results.xml']) shutil.copy('./environment.xml', './report/temp') os.system(f'allure serve ./report/temp') elif REPORT_TYPE == 'tm': pytest.main(['-vs', '--pytest-tmreport-name=testReport.html', '--pytest-tmreport-path=./report/tmreport']) webbrowser.open_new_tab(os.getcwd() + '/report/tmreport/testReport.html')

代码说明:

主程序入口

if __name__ == '__main__':

  • Python 标准写法,表示当脚本作为主程序运行时才执行以下代码块。
  • 防止模块被其他脚本导入时意外执行测试逻辑。

Allure报告处理逻辑

if REPORT_TYPE == 'allure': pytest.main( ['-s', '-v', '--alluredir=./report/temp', './testcase', '--clean-alluredir', '--junitxml=./report/results.xml'])

参数说明:

参数

含义

-s输出所有打印信息(不屏蔽 stdout)
-v显示详细测试结果
--alluredir=./report/temp将 Allure 报告数据保存到指定目录
./testcase指定测试用例所在目录
--clean-alluredir在每次运行前清空之前的报告数据
--junitxml=./report/results.xml生成 JUnit XML 格式的测试结果文件
shutil.copy('./environment.xml', './report/temp')
  • 复制环境信息文件environment.xml到报告目录中。

  • Allure 报告会读取此文件并显示当前测试环境信息。

os.system(f'allure serve ./report/temp')
  • 使用 Allure CLI 命令启动本地服务器并展示生成的报告页面。

  • 会在默认浏览器中自动打开报告。

2.测试用例 testcase

商务管理代码示例:

import allure import pytest from common.readyaml import get_testcase_yaml from base.apiutil_business import RequestBase from base.generateId import m_id, c_id # 注意:业务场景的接口测试要调用base目录下的apiutil_business文件 @allure.feature(next(m_id) + '电子商务管理系统(业务场景)') class TestEBusinessScenario: @allure.story(next(c_id) + '商品列表到下单支付流程') @pytest.mark.parametrize('case_info', get_testcase_yaml('./testcase/Business interface/BusinessScenario.yml')) def test_business_scenario(self, case_info): allure.dynamic.title(case_info['baseInfo']['api_name']) RequestBase().specification_yaml(case_info)

代码说明:

@pytest.mark.parametrize参数化装饰器

@pytest.mark.parametrize('case_info', get_testcase_yaml('./testcase/Business interface/BusinessScenario.yml'))

作用:

  • 实现参数化测试,即一个测试方法可以运行多组不同的输入数据。

  • 每一组case_info数据都会触发一次完整的测试执行。

参数说明:

  • 'case_info':表示每组测试数据的变量名,在测试函数中作为参数使用。

  • get_testcase_yaml(...):调用函数读取指定路径下的 YAML 文件内容,返回一个包含多个测试用例的列表。

YAML文件内容

- baseInfo: api_name: 商品详情 url: /coupApply/cms/productDetail method: post header: Content-Type: application/json;charset=UTF-8

测试方法定义

def test_business_scenario(self, case_info):
  • 这是一个pytest 测试方法,每个参数化的case_info都会触发一次该方法的执行。

  • self表示这是类中的一个实例方法(属于TestEBusinessScenario类)。

  • case_info是从 YAML 文件中加载的一条测试用例的数据字典。

动态设置 Allure 报告标题

allure.dynamic.title(case_info['baseInfo']['api_name'])

作用:

  • 在生成的 Allure 报告中,为当前测试用例设置一个可读性更强的标题。

  • 标题内容来自于 YAML 文件中baseInfo.api_name字段。

示例效果:

  • 如果api_name"获取商品列表",那么在报告中显示的用例名称就是这个值。

执行接口请求和校验

RequestBase().specification_yaml(case_info)

作用:

  • 创建RequestBase实例,并调用其specification_yaml()方法。

  • 该方法接收当前的测试用例数据case_info,并根据其中的配置(如 URL、方法、请求头、预期结果等)发送 HTTP 请求。

  • 同时会进行响应断言、日志记录等操作,完成整个接口测试流程。

3.接口测试specification_yaml()方法

代码示例

def specification_yaml(self, base_info, test_case): """ 接口请求处理基本方法 :param base_info: yaml文件里面的baseInfo :param test_case: yaml文件里面的testCase :return: """ try: params_type = ['data', 'json', 'params'] url_host = self.conf.get_section_for_data('api_envi', 'host') api_name = base_info['api_name'] allure.attach(api_name, f'接口名称:{api_name}', allure.attachment_type.TEXT) url = url_host + base_info['url'] allure.attach(api_name, f'接口地址:{url}', allure.attachment_type.TEXT) method = base_info['method'] allure.attach(api_name, f'请求方法:{method}', allure.attachment_type.TEXT) header = self.replace_load(base_info['header']) allure.attach(api_name, f'请求头:{header}', allure.attachment_type.TEXT) # 处理cookie cookie = None if base_info.get('cookies') is not None: cookie = eval(self.replace_load(base_info['cookies'])) case_name = test_case.pop('case_name') allure.attach(api_name, f'测试用例名称:{case_name}', allure.attachment_type.TEXT) # 处理断言 val = self.replace_load(test_case.get('validation')) test_case['validation'] = val validation = eval(test_case.pop('validation')) # 处理参数提取 extract = test_case.pop('extract', None) extract_list = test_case.pop('extract_list', None) # 处理接口的请求参数 for key, value in test_case.items(): if key in params_type: test_case[key] = self.replace_load(value) # 处理文件上传接口 file, files = test_case.pop('files', None), None if file is not None: for fk, fv in file.items(): allure.attach(json.dumps(file), '导入文件') files = {fk: open(fv, mode='rb')} res = self.run.run_main(name=api_name, url=url, case_name=case_name, header=header, method=method, file=files, cookies=cookie, **test_case) status_code = res.status_code allure.attach(self.allure_attach_response(res.json()), '接口响应信息', allure.attachment_type.TEXT) try: res_json = json.loads(res.text) # 把json格式转换成字典字典 if extract is not None: self.extract_data(extract, res.text) if extract_list is not None: self.extract_data_list(extract_list, res.text) # 处理断言 self.asserts.assert_result(validation, res_json, status_code) except JSONDecodeError as js: logs.error('系统异常或接口未请求!') raise js except Exception as e: logs.error(e) raise e except Exception as e: raise e

specification_yaml方法解析

该方法用于处理 YAML 文件中定义的接口测试用例,包括请求参数构造、动态变量替换、接口调用、响应断言以及数据提取等核心功能。

方法定义

def specification_yaml(self, base_info, test_case): """ 接口请求处理基本方法 :param base_info: yaml文件里面的baseInfo(接口基本信息) :param test_case: yaml文件里面的testCase(测试用例数据) :return: 无返回值,但通过断言验证接口行为 """

步骤详解

1. 定义常量与基础配置

params_type = ['data', 'json', 'params'] url_host = self.conf.get_section_for_data('api_envi', 'host')
  • params_type:表示请求参数可能包含的类型字段。

  • url_host:从配置文件中获取当前环境的主机地址。

2. 提取接口基本信息并添加 Allure 报告附件

api_name = base_info['api_name'] allure.attach(api_name, f'接口名称:{api_name}', allure.attachment_type.TEXT) url = url_host + base_info['url'] allure.attach(api_name, f'接口地址:{url}', allure.attachment_type.TEXT) method = base_info['method'] allure.attach(api_name, f'请求方法:{method}', allure.attachment_type.TEXT)
  • base_info中提取接口名称、URL 和请求方法。

  • 使用allure.attach将这些信息附加到 Allure 报告中,便于测试结果查看。

3. 处理请求头和 Cookie

header = self.replace_load(base_info['header']) allure.attach(api_name, f'请求头:{header}', allure.attachment_type.TEXT) cookie = None if base_info.get('cookies') is not None: cookie = eval(self.replace_load(base_info['cookies']))
  • 调用replace_load对 header 进行变量替换。

  • 如果存在 cookies,则同样进行替换并使用eval转换为字典格式

4. 提取测试用例名称并添加报告附件

case_name = test_case.pop('case_name') allure.attach(api_name, f'测试用例名称:{case_name}', allure.attachment_type.TEXT)
  • test_case中提取用例名称并删除原始字段。

  • 添加到 Allure 报告中。

5. 处理断言逻辑

val = self.replace_load(test_case.get('validation')) test_case['validation'] = val validation = eval(test_case.pop('validation'))
  • 替换断言表达式中的变量。

  • 使用eval执行断言表达式,生成实际断言规则。

6. 提取数据字段(extract / extract_list)

extract = test_case.pop('extract', None) extract_list = test_case.pop('extract_list', None)
  • test_case中提取需要提取的字段名,供后续从响应中提取数据。

7. 处理请求参数(data/json/params)

for key, value in test_case.items(): if key in params_type: test_case[key] = self.replace_load(value)
  • 遍历所有测试用例参数,若为datajsonparams类型,则对其值进行变量替换。

8. 处理文件上传

file, files = test_case.pop('files', None), None if file is not None: for fk, fv in file.items(): allure.attach(json.dumps(file), '导入文件') files = {fk: open(fv, mode='rb')}
  • 如果存在files字段,表示是文件上传接口。

  • 使用open(..., mode='rb')读取文件内容,并附加到 Allure 报告中。

9. 发送接口请求

res = self.run.run_main(name=api_name, url=url, case_name=case_name, header=header, method=method, file=files, cookies=cookie, **test_case) status_code = res.status_code allure.attach(self.allure_attach_response(res.json()), '接口响应信息', allure.attachment_type.TEXT)
  • 调用run_main方法发送请求。

  • 获取状态码和响应内容。

  • 将响应内容附加到 Allure 报告中。

10. 处理响应与断言

try: res_json = json.loads(res.text) # 把json格式转换成字典 if extract is not None: self.extract_data(extract, res.text) if extract_list is not None: self.extract_data_list(extract_list, res.text) # 处理断言 self.asserts.assert_result(validation, res_json, status_code) except JSONDecodeError as js: logs.error('系统异常或接口未请求!') raise js except Exception as e: logs.error(e) raise e
  • 将响应内容转为 JSON 字典。

  • 若有extractextract_list,则调用对应方法提取数据。

  • 使用断言方法对响应结果进行验证。

11. 异常捕获

except Exception as e: raise e
  • 捕获所有异常并重新抛出,确保测试框架可以正确识别失败情况。

4.断言判断

相等断言模式代码示例

def equal_assert(self, expected_results, actual_results, status_code=None): """ 相等断言模式 :param expected_results: 预期结果,yaml文件validation值 :param actual_results: 接口实际响应结果 :return: """ flag = 0 if isinstance(actual_results, dict) and isinstance(expected_results, dict): # 找出实际结果与预期结果共同的key common_keys = list(expected_results.keys() & actual_results.keys())[0] # 根据相同的key去实际结果中获取,并重新生成一个实际结果的字典 new_actual_results = {common_keys: actual_results[common_keys]} eq_assert = operator.eq(new_actual_results, expected_results) if eq_assert: logs.info(f"相等断言成功:接口实际结果:{new_actual_results},等于预期结果:" + str(expected_results)) allure.attach(f"预期结果:{str(expected_results)}\n实际结果:{new_actual_results}", '相等断言结果:成功', attachment_type=allure.attachment_type.TEXT) else: flag += 1 logs.error(f"相等断言失败:接口实际结果{new_actual_results},不等于预期结果:" + str(expected_results)) allure.attach(f"预期结果:{str(expected_results)}\n实际结果:{new_actual_results}", '相等断言结果:失败', attachment_type=allure.attachment_type.TEXT) else: raise TypeError('相等断言--类型错误,预期结果和接口实际响应结果必须为字典类型!') return flag

方法说明

方法名称

def equal_assert(self, expected_results, actual_results, status_code=None)

功能描述

该方法用于实现相等断言(Equal Assertion),即验证接口返回的实际结果中某个字段的值是否完全等于预期值

通过对比两个字典对象(实际结果与预期结果)中的指定字段内容,判断其是否完全一致。

示例场景

  • 验证接口返回的code是否为200

  • 验证响应中的user.id是否等于预期值

  • 验证嵌套结构中的某个字段是否完全匹配预期值

参数说明

参数名类型描述
expected_resultsdict

预期结果,通常来自 YAML 文件中的validation字段,例如:{"code": 200}

actual_resultsdict

接口实际返回的 JSON 响应体

statuc_codeint(可选)

HTTP 状态码(当前未使用,且参数名存在拼写错误)

返回值说明

  • 返回一个整数类型flag

    • 0表示断言成功

    • 1表示断言失败

执行逻辑详解

1. 初始化标志位

flag = 0

默认表示所有断言通过。

2. 类型校验

if isinstance(actual_results, dict) and isinstance(expected_results, dict):
  • 如果传入的actual_resultsexpected_results不是字典类型,则抛出异常:

raise TypeError('相等断言--类型错误,预期结果和接口实际响应结果必须为字典类型!')

3. 获取公共 key(要比较的字段)

common_keys = list(expected_results.keys() & actual_results.keys())[0]
  • 使用集合运算符&找出两个字典共有的 key。

  • 取第一个作为比较字段(⚠️ 当前仅支持单字段比较)。

4. 构造新的实际结果字典

new_actual_results = {common_keys: actual_results[common_keys]}
  • 从实际响应中提取对应字段的值,构造一个新的字典。

5. 使用 operator.eq 进行深度比较

eq_assert = operator.eq(new_actual_results, expected_results)
  • operator.eq()是 Python 的深度比较函数,可以比较复杂数据结构(如嵌套字典、列表等)是否完全相同。

  • 相比==更加严格和可靠。

6. 成功/失败处理

成功时:

logs.info("相等断言成功...") allure.attach(...)
  • 记录日志并附加到 Allure 报告中。

失败时:

flag += 1 logs.error("相等断言失败...") allure.attach(...)
  • 标记失败,记录错误日志,并将断言信息附加到报告中

四.测试报告

Allure报告部分截图

测试套分布

测试涵盖了三个主要的测试套,分别是testcase.Single interface(单接口测试套)、testcase.Business interface(业务接口测试套)和testcase.ProductManager(产品管理测试套)。各测试套的用例均全部通过,说明不同类型的接口及业务场景相关功能都能正常工作。

总结

本次基于 Allure 生成的测试报告,从多方面展示了测试成果。100% 的通过率以及各测试场景、接口的良好表现可以充分说明所测试系统的功能稳定和接口健壮。同时,Allure 报告清晰的结构和丰富的信息也为后续测试分析、问题定位及项目质量评估提供了有力支持。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/5 18:43:34

Memcached Session Manager高级特性:自定义序列化与扩展开发指南

Memcached Session Manager高级特性:自定义序列化与扩展开发指南 【免费下载链接】memcached-session-manager A tomcat session manager that backups sessions in memcached and pulls them from there if asked for unknown sessions 项目地址: https://gitcod…

作者头像 李华
网站建设 2026/7/5 18:43:22

todo[bot]完全指南:10分钟快速配置你的GitHub项目自动化工作流

todo[bot]完全指南:10分钟快速配置你的GitHub项目自动化工作流 【免费下载链接】todo 🤖✅ GitHub App that creates new issues from actionable comments in your code. 项目地址: https://gitcode.com/gh_mirrors/to/todo todo[bot]是一款基于…

作者头像 李华
网站建设 2026/7/5 18:42:56

How-To: Using the N* Stack, part 2

在上一篇文章当中,我们建立了一个基于 NHibernate 和 Ninject 的 ASP.NET MVC 应用程序,设置了解决方案的结构和一些第三方程序集的引用。 在本篇中,我们将介绍持久化对象模型的建立。持久化对象模型是一组对象,用于我们保存数据…

作者头像 李华
网站建设 2026/7/5 18:40:25

cann/asc-devkit矩阵计算空间API

MatmulGetTmpBufSize 【免费下载链接】asc-devkit 本项目是CANN 推出的昇腾AI处理器专用的算子程序开发语言,原生支持C和C标准规范,主要由类库和语言扩展层构成,提供多层级API,满足多维场景算子开发诉求。 项目地址: https://gi…

作者头像 李华