news 2026/9/26 4:14:23

星阅书城接口自动化测试实战揭秘

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
星阅书城接口自动化测试实战揭秘

1. 测试概述

1.1 测试背景

星阅书城平台的登录、用户管理、商品与订单等接口是业务主链路:用户必须先登录拿到Token凭证,才能新增、修改用户,下单链路则要按 "商品列表 → 商品详情 → 提交订单 → 订单支付 → 校验订单状态" 的顺序逐级依赖上一步的返回值。这类接口的特点是:

  1. 接口数量多、参数以表单和 JSON 混合提交(登录、用户管理是application/x-www-form-urlencoded,商品与订单是application/json);

  2. 接口之间存在强数据依赖(token、商品 ID、订单号要在用例之间传递);

  3. 异常分支容易被漏测(缺 token、缺必填参数、ID 不存在、密码错误等),人工回归一次要重复造数据、重复登录,成本高且容易漏。

为把这条主链路的回归从「手工点一遍」变成「一条命令跑完并自动通知结果」,搭建了本套接口自动化测试框架 :用例数据与代码分离(YAML 数据驱动),断言、请求、日志、报告、通知全部封装复用,既能在本地一条命令执行,也能接入 Jenkins 持续集成,每次构建自动产出 Allure 报告并推送飞书通知。

1.2 测试目标

序号目标衡量方式
1验证登录接口三种分支的正确性(成功 / 账号密码错 / 缺必填参数),并保证成功时下发可用凭证登录 4 条用例全部通过,响应字段逐项断言
2验证用户管理单接口的正向与异常分支(新增、修改、删除、查询)用户模块 10 条用例全部通过,异常分支均命中预期提示语
3验证「商品列表 → 详情 → 下单 → 支付 → 订单状态」整条业务链路能串起来跑通业务场景 5 条用例按顺序全部通过,链路变量正确传递
4验证接口关联能力:上一步的返回值能自动传给下一步,无需人工改数据extract/extract_list+${}占位符生效,链路无需硬编码
5建立可持续回归的工程能力:一条命令执行、自动出报告、自动通知、可接入 CIpytest一条命令跑完 19 条;Jenkins 构建成功并产出 Allure 报告 + 飞书通知
6通过自动化用例反过来发现被测系统的问题见第 6 章「缺陷分析」

1.3 测试范围

本次覆盖(3 个模块 / 9 个接口 / 19 条用例)

模块接口方法传参方式用例数
登录/dar/user/loginPOSTform 表单4
用户管理(单接口)/dar/user/addUserPOSTform 表单4
用户管理(单接口)/dar/user/updateUserPOSTform 表单1
用户管理(单接口)/dar/user/deleteUserPOSTform 表单4
用户管理(单接口)/dar/user/queryUserPOSTform 表单1
下单流程(业务链路)/coupApply/cms/goodsListGETURL 参数1
下单流程(业务链路)/coupApply/cms/productDetailPOSTJSON1
下单流程(业务链路)/coupApply/cms/placeAnOrderPOSTJSON1
下单流程(业务链路)/coupApply/cms/orderPayPOSTJSON1
下单流程(业务链路)/coupApply/cms/checkOrderStatusPOSTJSON1

2.接口测试用例

从单接口、业务逻辑接口、接口安全性多方面设计接口测试用例。

单接口测试用例:考虑正向,覆盖所有的必选参数、组合非必选参数以及边界值。反向考虑空数据和特殊值。

业务逻辑接口测试用例:注意上下接口之间有关联参数的接口

同时也需要考虑接口的安全性:未登录、特殊权限用户、参数加密等

3.接口自动化测试框架设计

3.1测试框架:Python+Pytest+Requests

3.2项目结构:

3.3环境依赖

3.4yaml文件设计测试用例

选择YAML文件管理接口地址、请求方式、请求头、请求参数、变量提取规则和预期结果。

以用户下单的接口为例:

- case_id: order_create_001 title: 已登录用户购买一本已上架图书 request: method: POST path: /api/orders headers: Authorization: "Bearer ${user_token}" json: book_id: "${book_id}" quantity: 1 validate: - eq: [status_code, 200] - eq: [body.code, 0] - eq: [body.data.status, PENDING_PAYMENT] extract: order_id: $.data.id

5.核心功能

5.1请求封装

请求封装的目的,是统一处理基础地址、超时时间、公共请求头、日志、报告附件和异常信息,而不是把业务断言全部塞进一个方法。

import json import requests import allure SENSITIVE_FIELDS = { "password", "token", "access_token", "refresh_token", "authorization", "cookie", "set-cookie", } def mask(data): if isinstance(data, dict): return { key: "***" if key.lower() in SENSITIVE_FIELDS else mask(value) for key, value in data.items() } if isinstance(data, list): return [mask(item) for item in data] return data class ApiClient: def __init__(self, base_url, timeout=10): self.base_url = base_url.rstrip("/") self.timeout = timeout self.session = requests.Session() def request(self, method, path, **kwargs): url = f"{self.base_url}/{path.lstrip('/')}" kwargs.setdefault("timeout", self.timeout) response = self.session.request(method.upper(), url, **kwargs) request_info = { "method": method.upper(), "url": url, "headers": mask(kwargs.get("headers", {})), "json": mask(kwargs.get("json", {})), } allure.attach( json.dumps(request_info, ensure_ascii=False, indent=2), "请求信息", allure.attachment_type.JSON, ) try: response_body = json.dumps( mask(response.json()), ensure_ascii=False, indent=2 ) except ValueError: response_body = response.text[:5000] allure.attach(response_body, "响应体", allure.attachment_type.TEXT) return response

这里没有统一调用raise_for_status(),因为 400、401、403 等状态本身也可能是异常场景的预期结果,应交给用例断言。另外,不建议对下单和支付等非幂等 POST 请求进行无条件自动重试。网络超时并不代表服务端没有处理请求,盲目重试可能产生重复订单或重复扣款。确实需要重试时,应由服务端提供幂等键,并在测试中验证幂等行为。

5.2权与 Token 管理

Token 管理是接口自动化中最容易引入共享状态的地方。比较稳妥的处理方式如下:

  • 测试账号和密码通过环境变量或 CI 凭据系统注入,不写入 Git 仓库。

  • 使用 Session 级 Fixture 登录一次,并把 Token 放入当前进程的内存上下文。

  • 用户端和管理端分别创建客户端,避免管理员 Token 被普通用户用例误用。

  • 请求日志和 Allure 附件中的 Token、Cookie、密码必须脱敏。

  • Token 过期、伪造 Token 等异常用例使用独立客户端,不修改公共客户端。

import pytest @pytest.fixture(scope="session") def user_client(config): client = ApiClient(config.base_url) response = client.request( "POST", "/api/login", json={"username": config.username, "password": config.password}, ) body = response.json() assert response.status_code == 200 assert body["code"] == 0 token = body["data"]["token"] client.session.headers.update({"Authorization": f"Bearer {token}"}) return client

有些框架会把 Token 写入extract.yaml。这种方式便于观察,但容易残留上一次执行的数据,也不适合并行运行。如果暂时沿用该方案,至少应在测试会话开始时清空动态数据;更推荐把静态 YAML 保持为只读,动态参数保存在 Fixture 或运行时上下文中。

5.3参数化与接口关联

YAML 数据读取后,可以通过pytest.mark.parametrize生成多条测试:

import pytest cases = load_yaml("testcase/order/create_order.yaml") @pytest.mark.parametrize("case", cases, ids=lambda case: case["case_id"]) def test_create_order(case, api_client, runtime_context): resolved_case = render_variables(case, runtime_context) response = api_client.request(**resolved_case["request"]) assert_response(response, resolved_case["validate"]) extract_variables(response.json(), resolved_case.get("extract"), runtime_context)

关联参数通常通过 JSONPath 提取:

  • 登录后提取user_token;

  • 创建图书后提取book_id;

  • 创建订单后提取order_id;

  • 支付完成后提取payment_id。

提取前要先完成接口成功断言;提取失败时应立即报告变量名和 JSONPath,而不是把空值带到下一个接口,导致后续错误难以定位。

5.4测试数据准备和清理

测试数据如果管理不当,会导致用例“单独运行成功,整套执行失败”。项目采用以下原则:

  1. 每条独立用例创建自己的前置数据;

  2. 用户名、手机号、ISBN 等唯一字段加入run_id或 UUID;

  3. 使用 Fixture 的yield在用例结束后清理数据;

  4. 优先通过业务接口清理,数据库删除只作为测试环境的兜底方式;

  5. 记录本次执行创建的资源编号,并按“支付记录 → 订单明细 → 订单 → 图书 → 用户”的逆序清理;

  6. 即使用例失败,也要在 Fixture 终结器或流水线的always/post阶段执行清理。

from uuid import uuid4 import pytest @pytest.fixture def new_user(api_client): suffix = uuid4().hex[:8] payload = { "username": f"api_user_{suffix}", "password": "TestPassword123!", } response = api_client.request("POST", "/api/users", json=payload) user_id = response.json()["data"]["id"] yield {"id": user_id, **payload} api_client.request("DELETE", f"/api/test-support/users/{user_id}")

测试环境最好提供受权限保护的测试数据清理接口。如果只能直接操作数据库,应限制为测试库、使用最小权限账号,并在删除前校验目标记录带有本次运行的唯一标识。

5.5断言:数据库校验

HTTP 200 只能说明请求被服务器接受,不能证明业务数据正确。例如支付接口返回成功,但可能出现以下问题:

  • 订单状态仍然是“待支付”;

  • 支付流水没有落库;

  • 库存没有扣减或被重复扣减;

  • 接口返回金额与订单实际金额不一致。

因此,核心链路采用“接口断言 + 数据库断言”的双重校验:

操作接口断言数据库断言
用户注册返回用户编号、业务码成功用户表存在且密码未明文保存
图书上架返回状态为 ON_SALE图书状态、上架时间正确
创建订单返回订单号和待支付状态订单主表、明细表、金额正确
支付订单返回支付成功订单状态、支付流水、库存变化一致
重复支付返回已支付或幂等结果只有一条有效支付记录,库存只扣一次

库存校验不应只判断某个固定值,而应比较操作前后的变化:

before_stock = db.query_one( "SELECT stock FROM book WHERE id = %s", (book_id,) )["stock"] pay_order(order_id) after_stock = db.query_one( "SELECT stock FROM book WHERE id = %s", (book_id,) )["stock"] assert after_stock == before_stock - quantity

SQL 必须参数化,数据库账号应遵循最小权限原则。断言应集中在可观察的业务结果上,避免过度依赖无关的内部实现字段,否则一次正常的数据库重构就可能让大量测试失效。

5.6Allure测试报告与Jenkins持续集成

本项目以 Allure 作为主要测试报告,

python -m pytest --alluredir=report/allure-results --clean-alluredir allure generate report/allure-results -o report/allure-report --clean

jenkins持续集成:

5.7通过接入飞书CLI测试结果回传到飞书群聊

编写飞书 CLI通知脚本,上层接收报告路径、环境和报告地址等参数,底层通过飞书群机器人 Webhook 发送消息。feishu.py主要负责解析报告、计算统计数据、生成飞书文本或消息卡片、调用群机器人 Webhook,并把通知过程写入独立日志。

def send_fs_msg(content_str, at_all=True): """ 向飞书群机器人推送文本消息。 :param content_str: 消息正文 :param at_all: 是否 @所有人 :return: """ conf = OperationConfig() url = conf.get_feishu_conf('webhook') secret = conf.get_feishu_conf('secret') or '' keyword = (conf.get_feishu_conf('keyword') or '').strip() if not url: logs.warning('conf.ini 里没有配置 [FEISHU] webhook,跳过飞书通知') return None timestamp, sign = generate_sign(secret) text = f'<at user_id="all">所有人</at>\n{content_str}' if at_all else content_str if keyword: text = f'{keyword}\n{text}' data = { "timestamp": timestamp, "sign": sign, "msg_type": "text", "content": {"text": text}, } res = requests.post(url, json=data, headers={'Content-Type': 'application/json;charset=utf-8'}, timeout=10) try: body = res.json() except ValueError: logs.error(f'飞书通知返回的不是 JSON:{res.text[:200]}') return None if body.get('code') == 0 or body.get('StatusCode') == 0: logs.info(f'飞书通知发送成功:{body}') else: logs.error(f'飞书通知发送失败(错误码 {body.get("code") or body.get("StatusCode")}):{body}') return body

6.总结

星阅书城接口自动化项目覆盖了注册、登录、图书管理、下单和支付等核心业务。框架通过 YAML 管理测试数据,使用 Pytest 完成参数化与 Fixture 管理,Requests 负责请求发送,JSONPath 实现接口关联,MySQL 验证最终数据一致性,再通过 Allure、Jenkins 或 Git完成结果展示和持续执行,并使用飞书 CLI 将测试摘要及时回传到项目群聊。

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

终端的庖丁解牛

根因 早期计算机是大型主机&#xff0c;主机本体放在机房&#xff0c;运算、存储全部由主机完成。操作人员不可能趴在主机上操作&#xff0c;于是就造出终端&#xff1a;本身没有算力&#xff0c;只负责接收人的输入、展示主机返回的输出&#xff0c;通过线缆连接远端主机。 到…

作者头像 李华
网站建设 2026/9/26 4:13:33

基于STM32单片机太阳能手机充电宝锂电池电量电压电流蓝牙无线APP/WiFi无线APP/摄像头视频监控/云平台设计S526

STM32-S526-太阳能充电宝USB输出输出电压电流功率锂电池电压电量欠压OLED屏声光提醒按键(无线方式选择)产品功能描述&#xff1a;本系统由STM32F103C8T6单片机核心板、OLED屏、&#xff08;无线蓝牙/无线WIFI/无线视频监控/联网云平台模块-可选&#xff09;、太阳能接口、锂电池…

作者头像 李华
网站建设 2026/9/26 4:13:30

SAP HANA SQLScript Watchpoints 深度解析,从变量监视到条件触发,掌握数据库调试器的精准定位能力

我们的 SAP S/4HANA 系统正在执行一批销售订单的价格计算。绝大多数订单的金额都正确,只有少数订单在经过折扣、税费和币种换算之后,最终金额与业务预期不一致。 负责价格计算的数据库存储过程并不复杂,里面包含订单明细查询、折扣规则匹配、金额汇总和税费计算等逻辑。不过…

作者头像 李华
网站建设 2026/9/26 4:12:02

WorkBuddy数据与隐私设置全指南:工作区授权、缓存迁移与记忆管理

1. 为什么数据与隐私设置值得单独拎出来讲很多人上手 WorkBuddy 的时候&#xff0c;注意力全在"怎么让它帮我干活"上——写代码、抓数据、生成网站、跑自动化工作流&#xff0c;恨不得第一天就把所有 Skill 都装一遍。结果用了两三周&#xff0c;突然发现工作台里堆了…

作者头像 李华