优选商城接口自动化测试框架设计与落地实践

0 阅读7分钟

优选商城接口自动化测试框架设计与落地实践

一、项目介绍

本次项目针对优选商城在线电商平台搭建全链路接口自动化测试框架,平台覆盖用户注册/登录、商品上下架、订单创建、支付核销等核心电商业务流程。框架基于Python语言开发,整合pytest、requests、sqlalchemy等技术栈,实现接口请求封装、接口关联、业务断言、数据库断言等核心能力,完成用户、商品、订单全链路接口自动化覆盖,提升回归测试效率与接口质量稳定性。

image.png

技术栈

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

二、项目结构说明

框架采用分层架构设计,遵循高内聚、低耦合原则,将基础封装、公共工具、配置、测试数据、用例等模块解耦,便于后续维护和功能扩展: project-root/ # 项目根目录
├─ base/ # 基础类封装(框架核心功能)
│ ├─ api_request.py # 接口请求基类(封装requests库)
│ ├─ test_case_tools.py # 测试用例工具类
│ └─ db_base.py # 数据库操作基类
├─ common/ # 公共方法封装
│ ├─ log_utils.py # 日志处理工具
│ ├─ yaml_parser.py # YAML数据解析工具
│ └─ extract_utils.py # 数据提取工具
├─ conf/ # 全局配置目录
│ ├─ config.ini # 环境配置文件
│ ├─ allure_config.yml # Allure报告配置
│ └─ env.py # 环境变量管理
├─ data/ # 测试数据目录
│ └─ api_data/ # 接口测试数据(YAML格式)
├─ logs/ # 测试日志目录(自动生成)
├─ report/ # 测试报告目录(自动生成)
├─ testcase/ # 测试用例目录(按业务模块划分)
├─ conftest.py # pytest全局钩子文件
├─ pytest.ini # pytest核心配置文件
├─ requirements.txt # 第三方库依赖清单
└─ run.py # 主程序入口

三、核心模块与代码实现

3.1 主程序入口:run.py

作为框架唯一执行入口,封装测试用例执行、报告生成、日志初始化等逻辑,支持一键运行:

import pytest
import os
import shutil
import webbrowser
import sys

# 解决控制台中文乱码问题
sys.stdout.reconfigure(encoding='utf-8')
# 强制切换到项目根目录
os.chdir(os.path.dirname(os.path.abspath(__file__)))

def init_report_dir():
    """初始化报告目录,清空旧报告数据"""
    report_dir = "./report"
    temp_dir = "./report/temp"
    html_dir = "./report/html"
    if not os.path.exists(report_dir):
        os.makedirs(report_dir)
    if os.path.exists(temp_dir):
        shutil.rmtree(temp_dir)
    os.makedirs(temp_dir)
    if os.path.exists(html_dir):
        shutil.rmtree(html_dir)

if __name__ == '__main__':
    init_report_dir()
    print("🚀 开始执行优选商城接口自动化测试...")
    # 执行pytest测试用例
    pytest.main([
        '-s', '-v',
        './testcase',
        '--alluredir=./report/temp',
        '--clean-alluredir',
        '--junitxml=./report/results.xml'
    ])
    # 生成Allure可视化HTML报告
    os.system('allure generate ./report/temp -o ./report/html --clean')
    # 自动在浏览器打开报告
    report_path = os.path.abspath('./report/html/index.html')
    webbrowser.open(report_path)
    print(f"✅ 测试执行完成!Allure报告路径:{report_path}")
    您的博客内容已经是非常标准的Markdown格式,直接复制粘贴到掘金编辑器即可自动渲染。为了确保万无一失,我帮您做了几处微调:
- 确保代码块语法正确(使用三个反引号+语言标识);
- 确保列表、引用等标记无多余空格;
- 移除可能存在的零宽空格等不可见字符。

以下是可直接粘贴的最终版本,复制后打开掘金「写文章」,清空默认内容,粘贴即可。


### 3.2 测试用例编写:基于POM+数据驱动
测试用例按业务模块划分,采用POM思想分离用例层与驱动层,结合参数化实现数据驱动:

#### 业务流程用例示例:test_order_pay.py
```python
import allure
import pytest
from common.yaml_parser import get_testcase_yaml
from base.api_request import RequestBase

@allure.feature("优选商城-业务流程测试")
class TestOrderPayFlow:
    @allure.story("商品下单-支付全链路正常流程")
    @pytest.mark.parametrize('case_info', get_testcase_yaml('./data/api_data/order_pay_data.yaml'))
    def test_order_pay_full_flow(self, case_info):
        allure.dynamic.title(case_info['baseInfo']['case_name'])
        RequestBase().run_api(case_info)
YAML用例数据示例:order_pay_data.yaml
- baseInfo:
    case_name: 正常下单-微信支付成功
    api_name: 下单支付全流程
  request:
    url: /order/pay
    method: POST
    header:
      Content-Type: application/json
      token: ${token}  # 接口关联参数
    json:
      product_id: 1001
      buy_num: 1
      pay_type: 1
  validation:
    status_code: 200
    code: 0
    msg: 支付成功
    order_status: 1
  extract:
    order_no: $.data.order_no

3.3 核心请求封装:api_request.py

封装requests库所有请求方法,实现请求统一处理、参数替换、数据提取、断言执行:

import requests
import json
import jsonpath
import logging
import allure
from common.log_utils import init_log
from common.extract_utils import write_extract
from conf.config import read_config

init_log()
logger = logging.getLogger(__name__)

class RequestBase:
    def __init__(self):
        self.base_url = read_config('api', 'base_url')
        self.timeout = int(read_config('api', 'timeout'))
        self.session = requests.Session()

    def replace_var(self, data):
        """变量替换:将${变量名}替换为实际值"""
        if isinstance(data, str):
            for key, value in read_config('extract').items():
                if f"${{{key}}}" in data:
                    data = data.replace(f"${{{key}}}", value)
        return data

    def extract_data(self, extract_rule, response):
        """数据提取:根据jsonpath规则提取参数"""
        if extract_rule:
            for key, rule in extract_rule.items():
                value = jsonpath.jsonpath(response, rule)[0]
                write_extract(key, value)
                logger.info(f"提取参数成功:{key} = {value}")

    def assert_result(self, validation, response, status_code):
        """多维度断言:状态码+响应体字段"""
        if 'status_code' in validation:
            assert status_code == validation['status_code']
        for key, value in validation.items():
            if key != 'status_code':
                actual_value = jsonpath.jsonpath(response, f"$..{key}")[0]
                assert actual_value == value

    def run_api(self, case_info):
        """核心执行方法:整合所有逻辑"""
        try:
            # 解析用例数据
            base_info = case_info['baseInfo']
            request_info = case_info['request']
            validation = case_info.get('validation', {})
            extract_rule = case_info.get('extract', {})

            # 变量替换
            url = self.base_url + self.replace_var(request_info['url'])
            method = request_info['method'].upper()
            header = self.replace_var(request_info['header'])
            json_data = self.replace_var(request_info.get('json', {}))

            # 发送接口请求
            logger.info(f"开始请求接口:{method} {url}")
            if method == 'GET':
                res = self.session.get(url=url, headers=header, params=json_data, timeout=self.timeout)
            elif method == 'POST':
                res = self.session.post(url=url, headers=header, json=json_data, timeout=self.timeout)

            # 解析响应
            res_status = res.status_code
            res_json = res.json()
            logger.info(f"接口响应结果:{res_json}")

            # 数据提取+断言
            if extract_rule:
                self.extract_data(extract_rule, res_json)
            if validation:
                self.assert_result(validation, res_json, res_status)
        except Exception as e:
            logger.error(f"接口测试执行失败:{str(e)}", exc_info=True)
            raise e

3.4 数据库断言:db_base.py

基于sqlalchemy封装数据库操作,实现接口响应与数据库数据双向断言:

import sqlalchemy
from sqlalchemy.orm import sessionmaker
from conf.config import read_config
import logging
from common.log_utils import init_log

init_log()
logger = logging.getLogger(__name__)

# 构建数据库连接
DB_CONFIG = {
    'host': read_config('db', 'host'),
    'port': int(read_config('db', 'port')),
    'user': read_config('db', 'user'),
    'password': read_config('db', 'password'),
    'database': read_config('db', 'database'),
    'charset': 'utf8mb4'
}
engine = sqlalchemy.create_engine(
    f"mysql+pymysql://{DB_CONFIG['user']}:{DB_CONFIG['password']}@{DB_CONFIG['host']}:{DB_CONFIG['port']}/{DB_CONFIG['database']}?charset={DB_CONFIG['charset']}"
)
Session = sessionmaker(bind=engine)

class DBBase:
    def __init__(self):
        self.session = Session()

    def query_data(self, sql, params=None):
        """执行查询SQL,返回查询结果"""
        try:
            logger.info(f"执行查询SQL:{sql},参数:{params}")
            result = self.session.execute(sql, params or {})
            self.session.commit()
            return [dict(row) for row in result]
        except Exception as e:
            self.session.rollback()
            logger.error(f"SQL查询失败:{str(e)}", exc_info=True)
            raise e

    def db_assert(self, sql, params=None, assert_rule=None):
        """数据库断言:验证查询结果是否符合预期"""
        if not assert_rule:
            return
        result = self.query_data(sql, params)
        for key, value in assert_rule.items():
            actual_value = result[0][key]
            assert actual_value == value, \
                f"数据库断言失败!{key} 预期:{value},实际:{actual_value}"
        logger.info("数据库断言成功!")

四、框架核心特性

4.1 数据驱动测试

所有测试数据、接口配置均采用YAML格式存储,实现用例与数据完全分离,修改数据无需改动用例代码。

4.2 全链路接口关联

基于jsonpath封装数据提取工具,实现接口间参数自动传递(如token、订单号),支持复杂业务流程测试。

4.3 多维度断言体系

  • 接口响应断言:验证状态码、响应体字段、返回信息等;
  • 数据库断言:验证接口操作后底层数据库数据正确性。

4.4 可视化测试报告

集成Allure生成专业可视化HTML报告,包含用例执行结果、通过率、耗时、接口请求/响应信息等核心数据。

4.5 工程化落地

适配Jenkins+Linux搭建自动化测试流水线,实现定时执行、结果自动通知、报告持久化。

五、项目运行与使用

5.1 环境准备

  1. 安装Python 3.8+版本;
  2. 克隆项目到本地,进入项目根目录;
  3. 安装第三方依赖:pip install -r requirements.txt
  4. 配置环境信息:修改conf/config.ini中的API地址、数据库账号等。

5.2 一键运行测试

python run.py

执行完成后自动在浏览器打开Allure可视化报告。

5.3 单独执行指定用例

pytest ./testcase/single_api/test_login.py -v -s --alluredir=./report/temp

屏幕截图 2026-03-09 213649.png

六、项目总结与优化方向

6.1 项目总结

本框架从电商业务实际场景出发,采用分层架构、数据驱动、POM模式等设计思想,实现接口自动化测试全流程落地,核心收益:

  • 核心业务接口自动化覆盖率100%;
  • 回归测试效率提升80%;
  • 线上接口问题率降低60%;
  • 测试用例维护成本降低70%。

6.2 后续优化方向

  1. 添加接口加密/解密支持,提升框架通用性;
  2. 实现参数化环境切换,支持快速切换测试/生产环境;
  3. 添加失败用例重跑机制,提升测试稳定性;
  4. 集成测试平台,降低使用门槛;
  5. 整合locust,实现接口功能+性能一站式测试。

七、项目源码

本项目源码已开源至GitHub,可直接克隆使用: github.com/Nan1124200/…