泰和昌商城接口自动化项目框架介绍

一、项目介绍

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

基于Python+pytest+sqlalchemy+requests+allure+jsonpath+yaml+Jenkins+Linux技术栈搭建的泰和昌商城接口自动化测试项目,针对在线购物商城的核心业务流程开展接口自动化验证。

该项目覆盖泰和昌商城用户注册、登录、商品查询、加入购物车、下单支付、商品上架/下架、订单管理等全流程接口,通过自动化测试实现接口功能的快速校验、回归测试与持续集成,保障商城接口的稳定性与正确性。

二、项目结构说明

css 复制代码
pythonproject/                  # 项目根目录
├── base/                       # 基础功能封装目录
│   ├── __init__.py             # 包初始化文件
│   ├── apiutil.py              # 接口请求工具类(封装requests)
│   ├── apiutil_business.py     # 业务层接口封装(组合接口逻辑)
│   ├── generateId.py           # 唯一ID生成工具(如订单号、商品ID)
│   ├── new_testcase_tools.py   # 测试用例辅助工具(数据构造、参数化)
│   ├── new_tools.ui            # 可视化工具界面(可选)
│   └── removefile.py           # 文件清理工具(日志/报告清理)
├── common/                     # 公共方法封装目录
│   ├── __init__.py             # 包初始化文件
│   ├── assertions.py           # 断言工具类(自定义断言规则)
│   ├── connection.py           # 数据库连接封装(SQLAlchemy)
│   ├── debugtalk.py            # 调试工具(接口调试、数据校验)
│   ├── dingRobot.py            # 钉钉机器人通知(测试结果推送)
│   ├── handleExcel.py          # Excel数据解析工具
│   ├── operationscsv.py        # CSV文件操作工具
│   ├── operxml.py              # XML文件解析工具
│   ├── Pjenkins.py             # Jenkins交互工具(任务触发、报告获取)
│   ├── readyaml.py             # YAML配置解析工具
│   ├── recordlog.py            # 日志记录工具(封装logging)
│   ├── semail.py               # 邮件发送工具(测试报告推送)
│   ├── sendrequest.py          # 接口请求发送封装
│   └── two_dimension_data.py   # 二维数据处理工具
├── conf/                       # 全局配置目录
│   ├── __init__.py             # 包初始化文件
│   ├── config.ini              # 环境配置文件(API地址、数据库信息)
│   ├── operationConfig.py      # 配置文件操作类
│   └── setting.py              # 全局常量配置(如超时时间、报告路径)
├── data/                       # 测试数据目录
│   ├── __init__.py             # 包初始化文件
│   ├── sql/                    # SQL脚本目录(测试数据初始化/清理)
│   ├── login_data.csv          # 登录接口CSV测试数据
│   ├── loginName.yaml          # 登录用户名YAML配置
│   ├── vehicleNo.csv           # 车辆编号测试数据(业务相关)
│   └── 测试数据.xls             # 通用业务Excel测试数据
├── logs/                       # 测试日志目录(自动生成)
│   └── test_20260107.log       # 按日期命名的日志文件
├── report/                     # 测试报告目录(自动生成)
│   ├── allureReport/           # Allure交互式报告目录
│   ├── temp/                   # 报告临时文件目录
│   ├── tmreport/               # TMReport表格报告目录
│   └── results.xml             # 测试结果XML文件
├── testcase/                   # 测试用例目录
│   ├── Business interface/     # 业务场景测试用例
│   │   ├── BusinessScenario.yml # 业务场景YAML配置
│   │   └── test_business_scenario.py # 业务场景测试类
│   ├── ProductManager/         # 商品管理接口测试
│   │   ├── apiType.yml         # 接口类型YAML配置
│   │   ├── commitOrder.yml     # 下单接口YAML配置
│   │   ├── getProductList.yml  # 商品列表接口YAML配置
│   │   ├── login_dw.yml        # 登录接口YAML配置
│   │   ├── orderPay.yml        # 订单支付接口YAML配置
│   │   ├── productDetail.yml   # 商品详情接口YAML配置
│   │   └── test_productList.py # 商品列表接口测试类
│   ├── Single interface/       # 单接口测试用例
│   │   ├── addUser.yml         # 新增用户接口YAML配置
│   │   ├── deleteUser.yml      # 删除用户接口YAML配置
│   │   ├── queryUser.yml       # 查询用户接口YAML配置
│   │   ├── test_debug_api.py   # 接口调试测试类
│   │   └── updateUser.yml      # 更新用户接口YAML配置
│   ├── __init__.py             # 包初始化文件
│   └── conftest.py             # pytest全局钩子文件
├── venv/                       # 虚拟环境目录(自动生成)
├── __init__.py                 # 项目根包初始化文件
├── conftest.py                 # 全局pytest钩子文件
├── environment.xml             # Allure报告环境信息文件
├── extract.yaml                # 接口依赖参数存储文件(如token、订单号)
├── pytest.ini                  # pytest配置文件(运行参数、插件配置)
├── requirements.txt            # 第三方库依赖清单(如pytest、requests)
└── run.py                      # 主程序入口(执行测试、生成报告)

三、核心代码

1.程序入口 run.py

代码如下:
python 复制代码
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')
代码说明:

程序主入口

python 复制代码
if __name__ == '__main__':
  • Python程序入口判断语句,区分脚本运行方式(作为主程序运行时执行代码;作为模块导入时不执行代码)
  • 是用来触发自动化测试的主流程(执行pytest用例、生成Allure报告等)
  • 保证该模块被导入时不会自动执行测试流程

Allure报告生成

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

|---------------------------------|------------------------|
| 参数 | 说明 |
| -s | 输出所有打印信息 |
| -v | 显示详细测试结果 |
| ---alluredir=./report/temp | 将 Allure 报告数据保存到指定目录 |
| ./testcase | 指定测试用例所在目录 |
| --clean-alluredir | 在每次运行前清空之前的报告数据 |
| --junitxml=./report/results.xml | 生成 JUnit XML 格式的测试结果文件 |

环境读取

python 复制代码
shutil.copy('./environment.xml', './report/temp')
  • 复制环境信息文件 environment.xml 到报告目录中。
  • Allure 报告会读取此文件并显示当前测试环境信息。

报告展示

  • 使用 Allure CLI 命令启动本地服务器并展示生成的报告页面。
  • 会在默认浏览器中自动打开报告。

2.testcase(测试用例)

商品管理代码示例:
python 复制代码
import allure
import pytest

from base.generateId import m_id, c_id
from base.apiutil import RequestBase
from common.readyaml import get_testcase_yaml


@allure.feature(next(m_id) + '商品管理(单接口)')
class TestLogin:

    @allure.story(next(c_id) + "获取商品列表")
    @pytest.mark.run(order=1)
    @pytest.mark.parametrize('base_info,testcase', get_testcase_yaml('./testcase/ProductManager/getProductList.yaml'))
    def test_get_product_list(self, base_info, testcase):
        allure.dynamic.title(testcase['case_name'])
        RequestBase().specification_yaml(base_info, testcase)

    @allure.story(next(c_id) + "获取商品详情信息")
    @pytest.mark.run(order=2)
    @pytest.mark.parametrize('base_info,testcase', get_testcase_yaml('./testcase/ProductManager/productDetail.yaml'))
    def test_get_product_detail(self, base_info, testcase):
        allure.dynamic.title(testcase['case_name'])
        RequestBase().specification_yaml(base_info, testcase)


    @allure.story(next(c_id) + "提交订单")
    @pytest.mark.run(order=3)
    @pytest.mark.parametrize('base_info,testcase', get_testcase_yaml('./testcase/ProductManager/commitOrder.yaml'))
    def test_commit_order(self, base_info, testcase):
        allure.dynamic.title(testcase['case_name'])
        RequestBase().specification_yaml(base_info, testcase)

    @allure.story(next(c_id) + "订单支付")
    @pytest.mark.run(order=4)
    @pytest.mark.parametrize('base_info,testcase', get_testcase_yaml('./testcase/ProductManager/orderPay.yaml'))
    def test_order_pay(self, base_info, testcase):
        allure.dynamic.title(testcase['case_name'])
        RequestBase().specification_yaml(base_info, testcase)

(1)@pytest.mark.run(order=N) 执行顺序装饰器

python 复制代码
  @pytest.mark.run(order=1)

作用:

  • 指定测试用例的执行顺序, N 为数字(数字越小,执行优先级越高)。
  • 在商品管理接口测试中,用于保证"获取商品列表→获取商品详情→提交订单→订单支付"的业务流程按真实场景顺序执行。

(2)@pytest.mark.parametrize() 参数化装饰器

python 复制代码
@pytest.mark.parametrize('base_info,testcase', get_testcase_yaml('./testcase/ProductManager/getProductList.yaml'))

作用:

  • 实现参数化测试,一个测试方法可运行多组不同的输入数据;
  • 每组 base_info 和 testcase 数据都会触发一次完整的测试执行,无需为不同用例编写重复代码。

参数说明:

  • 'base_info, testcase' :测试函数的参数名,分别接收接口基础配置(如API地址、请求方法)和测试用例数据(如入参、预期结果);
  • get_testcase_yaml(...) :调用自定义函数读取指定YAML文件内容,返回包含多组测试用例的列表,为参数化提供数据来源。

示例(YAML文件内容):

XML 复制代码
- baseInfo:
    api_name: 商品列表
    url: /coupApply/cms/goodsList
    method: Get
    header:
      Content-Type: application/x-www-form-urlencoded;charset=UTF-8
      token: ${get_extract_data(cookie)}
  testCase:
    - case_name: 获取商品列表
      params:
        msgType: getHandsetListOfCust
        page: 1
        size: 20
      validation:
        - contains: { 'error_code': '0000' }
      extract_list:
#        token: '"token": "(.*?)"'
        goodsId: $.goodsList[*].goodsId

(3)@allure.story() & allure.dynamic.title() Allure报告装饰器

python 复制代码
  @allure.story(next(c_id) + "获取商品详情信息")

作用:

  • @allure.story() :为测试用例添加业务场景标签(如"获取商品列表""提交订单"),在Allure报告中按场景分类展示;
  • allure.dynamic.title() :动态设置测试用例的标题(取自YAML中的 case_name ),让报告中用例名称更直观。

(4)RequestBase().specification_yaml() 接口请求核心方法

python 复制代码
 RequestBase().specification_yaml(base_info, testcase)

作用:

接收 base_info 和 testcase 参数,按YAML配置完成接口请求的发送、响应结果的校验(如状态码、返回数据断言),是接口自动化的核心执行逻辑。

核心测试用例函数说明:

测试方法定义

python 复制代码
def test_get_product_list(self, base_info, testcase):
  • 这是一个pytest测试方法,每个参数化的 base_info 和 testcase 组合数据,都会触发一次该方法的执行。
  • self 表示这是类中的一个实例方法(属于测试类)。
  • base_info 是从YAML文件中加载的接口基础配置字典(包含API地址、请求方法、请求头等核心配置)。
  • testcase 是从YAML文件中加载的单条测试用例的业务数据字典(包含接口入参、预期响应结果、断言规则等)。

|-------------------------|------------|---------|---------------------|
| 函数名 | 功能 | 执行顺序 | 数据来源 |
| test_get_product_list | 测试获取商品列表接口 | order=1 | getProductList.yaml |
| test_get_product_detail | 测试获取商品详情接口 | order=2 | productDetail.yaml |
| test_commit_order | 测试提交订单接口 | order=3 | commitOrder.yaml |
| test_order_pay | 测试订单支付接口 | order=4 | orderPay.yaml |

(5)Allure报告标题动态设置

python 复制代码
 allure.dynamic.title(testcase['case_name'])
作用:
  • 在生成的 Allure 报告中,为当前测试用例设置一个可读性更强的标题。
  • 标题内容来自于 YAML 文件中 case_name (获取商品列表)字段。

3.接口测试 specification_yaml

代码示例
python 复制代码
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()方法说明

该方法是接口自动化的核心处理方法,接收接口基础配置和测试用例数据,完成从请求构造到响应断言的全流程处理。

方法定义
python 复制代码
def specification_yaml(self, base_info, test_case):
        """
        接口请求处理基本方法
        :param base_info: yaml文件里面的baseInfo
        :param test_case: yaml文件里面的testCase
        :return:
        """
  • base_info 为从YAML文件读取的接口基础信息字典,包含接口名称、URL、请求方法等核心配置; test_case 为从YAML文件读取的测试用例数据字典,包含入参、断言规则、数据提取规则等。
  • 方法无显式返回值,通过断言校验接口响应是否符合预期,断言失败则抛出异常,标记测试用例失败。
详细说明

(1)定义常量与基础配置

python 复制代码
params_type = ['data', 'json', 'params']
url_host = self.conf.get_section_for_data('api_envi', 'host')
  • params_type :定义请求参数的常见类型字段( data / json / params ),用于后续识别并处理不同类型的接口入参。
  • url_host :从配置文件中读取当前测试环境的主机地址(如测试环境/生产环境的域名),实现环境配置的解耦。

(2)提取接口核心信息并附加到Allure报告

python 复制代码
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 中提取接口名称、接口路径、请求方法,拼接成完整的接口地址。
  • 通过 allure.attach 将这些核心信息附加到Allure报告中,便于测试结果分析时快速查看接口基础信息。

(3)处理请求头和Cookie

python 复制代码
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 )进行动态变量替换(如替换token、时间戳等动态参数)。
  • 若 base_info 中配置了 cookies ,先执行变量替换,再通过 eval 转换为字典格式,用于接口请求的Cookie认证。
  • 将处理后的请求头信息附加到Allure报告,便于调试接口认证问题。

(4)提取测试用例名称并附加到报告

python 复制代码
case_name = test_case.pop('case_name')
allure.attach(api_name, f'测试用例名称: {case_name}', allure.attachment_type.TEXT)
  • 通过 test_case.pop('case_name') 提取用例名称并从字典中移除该字段,避免后续参数处理时产生干扰。
  • 将用例名称附加到Allure报告,使报告中能清晰识别每个测试用例的业务场景。

(5) 处理断言规则与动态变量

python 复制代码
val = self.replace_load(test_case.get('validation'))
test_case['validation'] = val
validation = eval(test_case.pop('validation'))
  • 对 test_case 中的断言规则( validation )执行动态变量替换,支持基于前置接口返回值的动态断言。
  • 通过 eval 将断言规则的字符串转换为可执行的Python对象(如字典、列表),为后续接口响应断言做准备。

(6)处理参数提取规则

python 复制代码
extract = test_case.pop('extract', None)
extract_list = test_case.pop('extract_list', None)
  • 提取 test_case 中的数据提取规则( extract / extract_list ),用于从接口响应中提取关键数据(如token、订单号),供后续接口请求复用。
  • 若未配置提取规则,默认赋值为 None ,不影响后续流程执行。

(7)处理接口请求参数

python 复制代码
for key, value in test_case.items():
    if key in params_type:
        test_case[key] = self.replace_load(value)
  • 遍历 test_case 中的参数,若参数类型属于 params_type ( data / json / params ),则执行动态变量替换,支持动态入参(如使用前置接口提取的订单号)。

(8)处理文件上传接口

python 复制代码
file, files = test_case.pop('files', None), None
if file is not None:
    for fk, fv in file.items():
        allure.attach(json.dumps(file), '导入文件', allure.attachment_type.TEXT)
        files = {fk: open(fv, mode='rb')}
  • 若测试用例配置了文件上传参数( files ),先将文件信息附加到Allure报告,再通过 open 以二进制模式打开文件,构造文件上传的请求参数。

(9)发送接口请求并获取响应

python 复制代码
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 方法发送接口请求,传入处理后的请求头、Cookie、参数、文件等信息。
  • 获取接口响应状态码,并将响应数据格式化后附加到Allure报告,便于调试接口返回结果。

(10)数据提取与接口断言

python 复制代码
res_json = json.loads(res.text)
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)
  • 将接口响应的文本数据转换为字典格式( res_json ),方便数据提取和断言操作。
  • 若配置了数据提取规则,调用 extract_data / extract_data_list 从响应中提取关键数据并存储,供后续接口使用。
  • 调用 assert_result 方法,根据断言规则校验接口响应的状态码、返回数据是否符合预期,断言失败则抛出异常。

(11)异常处理

python 复制代码
except JSONDecodeError as js:
    logs.error('系统异常或接口未请求!')
    raise js
except Exception as e:
    logs.error(f'接口请求处理失败: {str(e)}')
    raise e
  • 捕获 JSONDecodeError (响应数据非JSON格式),输出错误日志并重新抛出异常,标记用例失败。
  • 捕获其他通用异常,记录详细错误信息并抛出,确保测试用例的异常能被pytest捕获并在报告中展示。

4.断言判断

包含断言
python 复制代码
def contains_assert(self, value, response, status_code):
        """
        字符串包含断言模式,断言预期结果的字符串是否包含在接口的响应信息中
        :param value: 预期结果,yaml文件的预期结果值
        :param response: 接口实际响应结果
        :param status_code: 响应状态码
        :return: 返回结果的状态标识
        """
        # 断言状态标识,0成功,其他失败
        flag = 0
        for assert_key, assert_value in value.items():
            if assert_key == "status_code":
                if assert_value != status_code:
                    flag += 1
                    allure.attach(f"预期结果:{assert_value}\n实际结果:{status_code}", '响应代码断言结果:失败',
                                  attachment_type=allure.attachment_type.TEXT)
                    logs.error("contains断言失败:接口返回码【%s】不等于【%s】" % (status_code, assert_value))
            else:
                resp_list = jsonpath.jsonpath(response, "$..%s" % assert_key)
                if isinstance(resp_list[0], str):
                    resp_list = ''.join(resp_list)
                if resp_list:
                    assert_value = None if assert_value.upper() == 'NONE' else assert_value
                    if assert_value in resp_list:
                        logs.info("字符串包含断言成功:预期结果【%s】,实际结果【%s】" % (assert_value, resp_list))
                    else:
                        flag = flag + 1
                        allure.attach(f"预期结果:{assert_value}\n实际结果:{resp_list}", '响应文本断言结果:失败',
                                      attachment_type=allure.attachment_type.TEXT)
                        logs.error("响应文本断言失败:预期结果为【%s】,实际结果为【%s】" % (assert_value, resp_list))
        return flag
方法说明

方法定义

python 复制代码
def contains_assert(self, value, response, status_code):
  """
    字符串包含断言模式,断言预期结果的字符串是否包含在接口的响应信息中
    :param value: 预期结果,yaml文件的预期结果值
    :param response: 接口实际响应结果
    :param status_code: 响应状态码
    :return: 返回结果的状态标识(0为成功,非0为失败)
    """
  • 该方法是字符串包含型断言工具,主要用于校验接口响应状态码是否符合预期,以及响应文本中是否包含指定的字符串内容。
  • 入参 value 为YAML配置的断言规则字典, response 为接口实际返回的响应数据, status_code 为接口响应状态码;返回值为状态标识, 0 代表断言成功,非 0 代表断言失败。
核心逻辑详解

(1)初始化断言状态标识

python 复制代码
flag = 0  # 断言状态标识,0成功,其他失败
  • 定义 flag 变量作为断言结果的标识,初始值为 0 (默认断言成功),若任意断言项失败, flag 会累加变为非0值。

(2)遍历断言规则执行校验

python 复制代码
for assert_key, assert_value in value.items():
  • 遍历 value 字典中的断言规则(键为断言类型,值为预期结果),支持同时校验多个断言项(如同时校验状态码和响应文本)。

(3) 响应状态码断言

python 复制代码
if assert_key == "status_code":
    if assert_value != status_code:
        flag += 1
        allure.attach(f"预期结果: {assert_value}\n实际结果: {status_code}", "响应代码断言结果:失败", attachment_type=allure.attachment_type.TEXT)
        logs.error("contains断言失败: 接口返回码【%s】不等于【%s】" % (status_code, assert_value)
  • 当断言键为 status_code 时,校验接口实际响应状态码是否与预期值一致。
  • 若不一致, flag 加1,通过 allure.attach 将断言失败信息附加到Allure报告,同时通过日志模块记录错误信息,便于问题排查。

(4) 响应文本包含断言

python 复制代码
else:
    resp_list = jsonpath.jsonpath(response, "$..%s" % assert_key)
    if isinstance(resp_list[0], str):
        resp_list = ''.join(resp_list)
    if resp_list:
        assert_value = None if assert_value.upper() == 'NONE' else assert_value
        if assert_value in resp_list:
            logs.info("字符串包含断言成功: 预期结果【%s】,实际结果【%s】" % (assert_value, resp_list))
        else:
            flag += 1
            allure.attach(f"预期结果: {assert_value}\n实际结果: {resp_list}", "响应文本断言结果:失败", attachment_type=allure.attachment_type.TEXT)
            logs.error("响应文本断言失败: 预期结果为【%s】,实际结果为【%s】" % (assert_value, resp_list))
  • JSONPath提取响应数据:通过 jsonpath.jsonpath 从接口响应 response 中,按 assert_key 对应的JSON路径提取目标数据,支持多层嵌套的JSON数据解析。
  • 字符串格式化:若提取的结果为字符串类型,通过 ''.join(resp_list) 将列表转换为字符串,便于后续包含校验。
  • 特殊值处理:若预期值为 NONE (不区分大小写),将 assert_value 赋值为 None ,支持"响应数据为空"的断言场景。
  • 包含校验:判断预期值是否包含在提取的响应数据中,若包含则记录成功日志;若不包含则 flag 加1,附加失败信息到Allure报告并记录错误日志

方法核心价值

  1. 多维度断言:同时支持状态码和响应文本的断言,满足接口自动化中最常见的两类校验需求。

  2. 可视化报告:断言失败时将预期/实际结果附加到Allure报告,直观展示失败原因,提升调试效率。

  3. 灵活的JSON解析:基于JSONPath提取响应数据,支持复杂嵌套的JSON结构解析,适配各类接口的响应格式。

断言接口响应信息中的body的任何属性值
python 复制代码
def assert_result(self, expected, response, status_code):
        """
        断言,通过断言all_flag标记,all_flag==0表示测试通过,否则为失败
        :param expected: 预期结果
        :param response: 实际响应结果
        :param status_code: 响应code码
        :return:
        """
        all_flag = 0
        try:
            logs.info("yaml文件预期结果:%s" % expected)
            # logs.info("实际结果:%s" % response)
            # all_flag = 0
            for yq in expected:
                for key, value in yq.items():
                    if key == "contains":
                        flag = self.contains_assert(value, response, status_code)
                        all_flag = all_flag + flag
                    elif key == "eq":
                        flag = self.equal_assert(value, response)
                        all_flag = all_flag + flag
                    elif key == 'ne':
                        flag = self.not_equal_assert(value, response)
                        all_flag = all_flag + flag
                    elif key == 'rv':
                        flag = self.assert_response_any(actual_results=response, expected_results=value)
                        all_flag = all_flag + flag
                    elif key == 'db':
                        flag = self.assert_mysql_data(value)
                        all_flag = all_flag + flag
                    else:
                        logs.error("不支持此种断言方式")

        except Exception as exceptions:
            logs.error('接口断言异常,请检查yaml预期结果值是否正确填写!')
            raise exceptions

        if all_flag == 0:
            logs.info("测试成功")
            assert True
        else:
            logs.error("测试失败")
            assert False
方法说明

方法定义

python 复制代码
def assert_result(self, expected, response, status_code):
    """
    统一断言入口方法,根据断言类型调用不同的断言函数,汇总断言结果
    :param expected: YAML配置的断言规则列表(包含多种断言类型)
    :param response: 接口实际响应结果
    :param status_code: 接口响应状态码
    :return: 无显式返回值,通过assert True/False标记测试用例成败
    """
  • 该方法是接口自动化的统一断言入口,支持根据YAML配置的断言类型(包含、相等、不等、任意属性、数据库校验等),自动调用对应的断言方法,并汇总所有断言结果,最终通过 assert 关键字标记测试用例的成败。
  • 是断言体系的"总控方法",整合了各类细分断言逻辑,简化了测试用例中断言的调用方式。
核心逻辑详解

(1)初始化总断言状态标识

python 复制代码
all_flag = 0  # 总断言标识,0表示所有断言通过,非0表示存在断言失败
  • 定义 all_flag 作为所有断言项的结果汇总标识,初始值为 0 ,每有一个断言项失败,该值会累加对应断言方法返回的非0值。

(2) 异常捕获与断言规则遍历

python 复制代码
try:
    logs.info(f'yaml文件预期结果: {expected}')
    for yq in expected:  # 遍历断言规则列表中的每一组断言配置
        for key, value in yq.items():  # 遍历单组断言的类型与规则
            # 根据断言类型调用对应断言方法
            if key == "contains":
                flag = self.contains_assert(value, response, status_code)
                all_flag = all_flag + flag
            elif key == "eq":
                flag = self.equal_assert(value, response)
                all_flag = all_flag + flag
            elif key == "ne":
                flag = self.not_equal_assert(value, response)
                all_flag = all_flag + flag
            elif key == "rv":
                flag = self.assert_response_any(actual_results=response, expected_results=value)
                all_flag = all_flag + flag
            elif key == "db":
                flag = self.assert_mysql_data(value)
                all_flag = all_flag + flag
            else:
                logs.error("不支持此种断言方式")
except Exception as exceptions:
    logs.error('接口断言异常,请检查yaml预期结果值是否正确填写!')
    raise exceptions
 
  • 日志记录:打印YAML配置的预期断言规则,便于调试时核对断言配置。
  • 双层遍历断言规则:

外层 for yq in expected 遍历断言规则列表(支持多组断言配置);

内层 for key, value in yq.items() 遍历单组断言的类型(key)与规则(value)。

  • 按类型调用断言方法:

断言类型(key) 调用方法 断言功能

contains contains_assert 字符串包含断言+状态码断言

eq equal_assert 等值断言(实际=预期)

ne not_equal_assert 不等值断言(实际≠预期)

rv assert_response_any 响应任意属性值断言

db assert_mysql_data 数据库数据校验断言

  • 不支持的断言类型:若配置了未定义的断言类型,记录错误日志,不影响其他断言项执行。
  • 异常处理:捕获断言过程中的所有异常(如配置格式错误、断言方法调用失败等),记录错误日志并重新抛出异常,确保异常能被pytest捕获。

(3)断言结果最终校验

python 复制代码
if all_flag == 0:
    logs.info("测试成功")
    assert True
else:
    logs.error("测试失败")
    assert False
  • 若 all_flag 为 0 ,说明所有断言项均通过,记录成功日志并执行 assert True ,标记测试用例成功。
  • 若 all_flag 非 0 ,说明存在断言失败项,记录失败日志并执行 assert False ,标记测试用例失败。

四、项目结语

本项目基于pytest+Allure技术栈搭建了泰和昌商城接口自动化测试框架,覆盖商品管理、订单支付、用户操作等电商核心业务的接口测试场景。框架通过数据驱动解耦测试用例与测试数据,借助动态变量替换适配接口的动态参数需求,依托多维度断言体系实现接口响应的精准校验,结合Allure完成测试报告的可视化输出,有效提升了商城接口测试的效率与回归测试的覆盖度。同时,框架支持与Jenkins集成实现持续测试,具备良好的可扩展性与维护性。未来将进一步集成接口性能监控等功能,持续提升框架的智能化与全链路测试能力,为泰和昌商城的系统稳定性提供更全面的测试保障。

相关推荐
运维之美@1 天前
linux主机ping不通问题排查
linux·运维·服务器
MyCollege19991 天前
以UEFI模式从U盘安装centos遇到空间不足
linux·运维·centos
草木红1 天前
Docker 和 portainer 安装
运维·docker·容器·portainer
云飞云共享云桌面1 天前
昆山精密机械工厂研发部门10个SolidWorks如何共享一台服务器来进行设计办公
运维·服务器·网络·人工智能·电脑
羑悻的小杀马特1 天前
【Linux篇章】穿越网络迷雾:揭开 HTTP 应用层协议的终极奥秘!从请求响应到实战编程,从静态网页到动态交互,一文带你全面吃透并征服 HTTP 协议,打造属于你的 Web 通信利刃!
linux·运维·网络·http·操作系统·网络通信
网安CILLE1 天前
Linux 命令大全(网络安全常用)
linux·运维·服务器·网络安全
b***25111 天前
圆柱锂电池双面点焊机:新能源制造的核心工艺装备
人工智能·自动化
vivo互联网技术1 天前
智取流量,效赢增长-拨测和融合流量管理业务赋能实践
运维
allway21 天前
Understanding and Resolving StrictModes Issues in SSHD Configuration
运维·服务器