目录
一、功能测试
1.1 测试用例总汇表
1.2 代表性用例展示
1.3 缺陷统计
1.4 重点缺陷描述
二、 UI自动化测试
2.1 技术选型
2.2 项目结构
2.3 核心代码实现
(1)Driver 工具类封装
(2)全局 Fixture 配置
(3)测试用例示例
2.4 遇到的问题及解决方案
问题一:类名直接调用实例方法报错
问题二:相对路径写错导致目录创建失败
问题三:创建目录和保存截图路径不一致
2.5 运行结果
三、 性能测试
3.1 测试工具
3.2 测试场景设计
3.3 测试结果
3.4 结果分析
3.5 性能结论
四、接口测试
4.1 测试工具与框架
4.2 框架搭建
4.2.1 项目目录结构
4.2.2 核心模块设计
4.3 测试用例示例
4.3.1 登录接口(参数化 + 异常场景)
4.3.2 商品列表接口(分页参数化)
4.3.3 购物车接口(Base64 编码 + 规格参数)
4.5 运行结果
本次代码存放的gitee仓库:https://gitee.com/sddsfsdf455/shop-xo_auto_test.git
本文是对 ShopXO v6.9.1 开源电商系统的一次完整测试实践,涵盖功能测试、UI 自动化、性能压测和接口自动化四个部分。
项目背景:ShopXO 是一款基于 ThinkPHP 开发的开源 B2C 电商商城系统,包含前台商品浏览、购物车、订单、会员中心,以及后台商品管理、订单管理、用户管理等功能。
测试环境:
- 前台地址:http://121.43.128.122/
- 后台地址:http://121.43.128.122/adminhchlav.php
- 系统版本:ShopXO v6.9.1
- 测试浏览器:Edge
测试范围:前台商城(首页 / 商品 / 搜索 / 注册登录 / 购物车 / 订单)、后台管理(商品 / 订单 / 用户)、前后台数据一致性、安全越权测试。
测试结论速览:共执行 57 条用例,成功 48 条,成功率 84.2%;发现缺陷 5 个(严重 1、一般 3、轻微 1)。核心浏览和登录功能正常,主要问题集中在第三方服务未配置(支付 / 短信 / 邮件)。
一、功能测试
1.1 测试用例总汇表
| 模块 | 用例数 | 成功 | 失败 | 阻塞 | 备注 |
|---|---|---|---|---|---|
| 首页 | 4 | 3 | 1 | 0 | 图片加载失败 |
| 商品 | 7 | 7 | 0 | 0 | |
| 搜索 | 3 | 3 | 0 | 0 | |
| 用户 | 8 | 7 | 1 | 0 | |
| 购物车 | 5 | 5 | 0 | 0 | |
| 订单 | 4 | 2 | 1 | 1 | 无支付方式 |
| 后台登录与首页 | 6 | 6 | 0 | 0 | |
| 后台商品管理 | 5 | 5 | 0 | 0 | |
| 后台订单管理 | 3 | 2 | 0 | 1 | |
| 前后台数据一致性 | 4 | 2 | 0 | 2 | |
| 安全 | 4 | 4 | 0 | 0 | |
| 兼容性 | 2 | 2 | 0 | 0 | |
| 后台用户管理 | 2 | 2 | 0 | 0 | |
| 合计 | 57 | 50 | 3 | 4 | 成功率 87.7% |
1.2 代表性用例展示
正确账号密码登录:
登陆成功:
加入购物车:
操作成功:
后台订单管理:
1.3 缺陷统计
| 严重程度 | 数量 |
|---|---|
| 严重 | 1 |
| 一般 | 3 |
| 轻微 | 1 |
| 合计 | 5 |
1.4 重点缺陷描述
BUG004(严重):订单结算页无支付方式,无法提交订单:
BUG002/003(一般):手机 / 邮箱注册验证码发送失败:
BUG001(轻微):首页图片加载失败 → 贴截图:
二、 UI自动化测试
2.1 技术选型
UI 自动化测试采用Python + Selenium + pytest技术栈:
- Selenium:Web 应用自动化测试框架,支持多浏览器驱动
- pytest:测试框架,提供 fixture 机制、断言和测试报告
- webdriver-manager:自动管理浏览器驱动,无需手动下载对应版本
2.2 项目结构
ShopXO_auto_test/ ├── common/ │ └── Utils.py # Driver 工具类封装 ├── images/ # 测试截图存放目录 ├── test/ │ ├── test_login_suc.py # 登录成功用例 │ ├── test_login_fail.py # 登录失败用例 │ ├── test_check_elements.py # 元素检查用例 │ ├── test_product_detail.py # 商品详情用例 │ └── test_shopcar_nologin.py# 未登录购物车用例 ├── conftest.py # pytest 全局 fixture └── pytest.ini # pytest 配置文件2.3 核心代码实现
(1)Driver 工具类封装
在common/Utils.py中封装Driver类,统一管理浏览器驱动的创建和截图功能:
import datetime import os.path import sys from selenium import webdriver from selenium.webdriver.edge.service import Service from webdriver_manager.microsoft import EdgeChromiumDriverManager class Driver: driver = "" def __init__(self): # 创建驱动对象,返回驱动实例 self.driver = webdriver.Edge(service=Service(EdgeChromiumDriverManager().install())) # 隐式等待两秒,防止页面元素加载失败 self.driver.implicitly_wait(2) @staticmethod def screen_shoot(driver): # dirname-->生成放置图片的images子文件夹 dirname = datetime.datetime.now().strftime("%Y-%m-%d") # 因为每天都会实时更新时间 所以会生成多个不同日期的子文件夹 if not os.path.exists("./images/" + dirname): os.mkdir("./images/" + dirname) # 生成图片存放路径和名称 filename = sys._getframe().f_back.f_code.co_name + datetime.datetime.now().strftime("%Y-%m-%d-%H%M%S") + ".png" driver.save_screenshot("./images/" + dirname + "/" + filename)(2)全局 Fixture 配置
在conftest.py中配置 session 级别的 fixture,整个测试会话只启动一次浏览器,所有用例执行完毕后自动关闭:
import pytest from common.Utils import Driver @pytest.fixture(scope="session") # 整个测试会话只启动一次浏览器 def driver(): driver_util = Driver() yield driver_util.driver # 所有测试跑完后,自动关闭浏览器 driver_util.driver.quit()(3)测试用例示例
以登录成功用例为例,演示元素定位、操作和截图:
import pytest from selenium.webdriver.common.by import By from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from common.Utils import Driver @pytest.mark.order(2) class Test_login_suc: def test_login_suc(self, driver): url = "http://121.43.128.122/" driver.get(url) driver.find_element(By.CSS_SELECTOR, "body > div.body-content-container > div.body-content-formal-container > div.am-container.slideall > div > div.mod-vip > div.member-login > a.am-btn-primary.btn.am-fl.am-radius").click() # 添加显示等待 灵活 wait = WebDriverWait(driver, 2) wait.until(EC.visibility_of_element_located((By.CSS_SELECTOR, "body > div.body-content-container > div.body-content-formal-container > div.am-g.user-login-container.am-padding-top-0.am-padding-bottom-0.theme-data-edit-event > div > div.am-container > div.am-u-sm-12.am-u-md-6.am-u-lg-4.user-right-container > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form"))) # 输入正确的账号密码 driver.find_element(By.CSS_SELECTOR, "body > div.body-content-container > div.body-content-formal-container > div.am-g.user-login-container.am-padding-top-0.am-padding-bottom-0.theme-data-edit-event > div > div.am-container > div.am-u-sm-12.am-u-md-6.am-u-lg-4.user-right-container > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div:nth-child(1) > input").send_keys("11") driver.find_element(By.CSS_SELECTOR, "body > div.body-content-container > div.body-content-formal-container > div.am-g.user-login-container.am-padding-top-0.am-padding-bottom-0.theme-data-edit-event > div > div.am-container > div.am-u-sm-12.am-u-md-6.am-u-lg-4.user-right-container > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div.am-form-group.am-form-group-refreshing.am-margin-top-main.am-form-input-material > input").send_keys("************") # 点击登录 driver.find_element(By.CSS_SELECTOR, "body > div.body-content-container > div.body-content-formal-container > div.am-g.user-login-container.am-padding-top-0.am-padding-bottom-0.theme-data-edit-event > div > div.am-container > div.am-u-sm-12.am-u-md-6.am-u-lg-4.user-right-container > div.am-radius-lg.am-background-white > div > div.am-tabs-bd.am-border-0 > div.am-tab-panel.am-active > form > div.am-form-group.am-form-group-refreshing.am-margin-top-lg.am-padding-0 > button").click() # 检查页面元素是否正常 driver.find_element(By.CSS_SELECTOR, "body > div.body-content-container > div.body-content-formal-container > div.am-container.slideall > div > div.mod-vip > div.m-baseinfo.am-flex.am-flex-items-center > div > span") driver.find_element(By.CSS_SELECTOR, "body > div.body-content-container > div.body-content-formal-container > div.am-container.slideall > div > div.mod-vip > div.m-baseinfo.am-flex.am-flex-items-center > a > img") Driver.screen_shoot(driver)2.4 遇到的问题及解决方案
在实现截图功能过程中,先后遇到三个问题,记录如下:
问题一:类名直接调用实例方法报错
报错信息:
TypeError: Driver.screen_shoot() missing 1 required positional argument: 'self'原因分析:screen_shoot()最初定义为实例方法,但测试中通过类名Driver.screen_shoot()直接调用。由于conftest.py的 fixture 返回的是driver_util.driver(WebDriver 实例),而非Driver类实例,无法通过实例调用该方法。
解决方案:将screen_shoot()改为静态方法(@staticmethod),并接收driver参数,调用时传入 WebDriver 实例:Driver.screen_shoot(driver)。
问题二:相对路径写错导致目录创建失败
报错信息:
FileNotFoundError: [WinError 3] 系统找不到指定的路径。: '../images/2026-09-28'原因分析:代码中使用了../images/(两个点),表示上一级目录下的 images 文件夹。而测试运行时工作目录是项目根目录,images 文件夹就在当前目录下,应该使用./images/(一个点)。
问题三:创建目录和保存截图路径不一致
现象:目录创建成功,但截图文件始终不生成,也不报错。
原因分析:创建目录时用的是./images/,但保存截图时误写为../images/,两处路径不一致。Selenium 的save_screenshot()失败时只返回False,不会抛出异常,因此难以察觉。
解决方案:将创建目录和保存截图的路径统一为./images/。
2.5 运行结果
执行pytest命令运行全部测试用例:
三、 性能测试
3.1 测试工具
本次性能测试采用Apache JMeter 5.5作为压测工具。JMeter 是一款开源的性能测试工具,支持对 HTTP、数据库等多种服务进行压力测试,能够模拟多用户并发访问,并通过聚合报告、响应时间趋势图、吞吐量趋势图等监听器全面采集性能指标。
测试计划中使用了jp@gc - Stepping Thread Group(阶梯线程组)来模拟用户量逐步递增的并发场景,更贴近真实用户逐步涌入系统的访问模式。
3.2 测试场景设计
针对 ShopXO 电商系统的核心高频页面,选取以下三个接口进行并发压测:
| 序号 | 接口名称 | 说明 |
|---|---|---|
| 1 | 首页 | 系统首页加载,包含商品推荐、轮播图等模块 |
| 2 | 商品列表 | 商品分类列表页,包含分页、筛选等数据查询 |
| 3 | 商品详情 | 单个商品详情页,包含商品信息、规格、图片等加载 |
同时配置了以下监听器用于结果采集与分析:
- jp@gc - Active Threads Over Time:监控活动线程数随时间的变化
- jp@gc - Response Times Over Time:监控响应时间随时间的变化趋势
- jp@gc - Transactions per Second:监控每秒事务数(吞吐量)变化
- 聚合报告:汇总各接口的核心性能指标
3.3 测试结果
本次压测共发起357个请求,全部请求均成功,异常率为0.00%。聚合报告核心数据如下:
| 接口名称 | 样本数 | 平均响应时间 (ms) | 中位数 (ms) | 90% 百分位 (ms) | 95% 百分位 (ms) | 99% 百分位 (ms) | 最小值 (ms) | 最大值 (ms) | 异常率 | 吞吐量 (次 / 秒) |
|---|---|---|---|---|---|---|---|---|---|---|
| 首页 | 127 | 4920 | 4194 | 8220 | 11022 | 13644 | 796 | 17059 | 0.00% | 1.7 |
| 商品列表 | 120 | 3493 | 2963 | 6541 | 7613 | 10152 | 552 | 15628 | 0.00% | 1.6 |
| 商品详情 | 110 | 3149 | 3012 | 5054 | 5495 | 6280 | 662 | 6444 | 0.00% | 1.5 |
| 总体 | 357 | 3895 | 3397 | 6502 | 8220 | 12074 | 552 | 17059 | 0.00% | 4.7 |
3.4 结果分析
(1)系统稳定性
三个接口的异常率均为 0.00%,说明在当前并发压力下系统能够稳定处理所有请求,未出现服务不可用或报错的情况,系统稳定性良好。
(2)响应时间分析
- 总体平均响应时间为3895ms(约 3.9 秒),中位数为3397ms,说明半数以上请求能在 3.4 秒内完成。
- 首页响应时间最长,平均 4920ms,95% 百分位达到 11022ms,最大响应时间 17059ms。原因是首页加载内容较多,包含轮播图、推荐商品、分类导航等多个模块,数据查询和资源加载开销较大。
- 商品列表平均响应时间 3493ms,表现居中。
- 商品详情响应时间最短,平均 3149ms,且 99% 百分位仅 6280ms,响应时间波动最小,接口性能最稳定。
(3)吞吐量分析
- 系统总体吞吐量为4.7 次 / 秒,其中首页 1.7 次 / 秒、商品列表 1.6 次 / 秒、商品详情 1.5 次 / 秒,三个接口吞吐量分布较为均衡。
- 首页由于响应时间较长,单位时间内完成的请求数相对受限,是影响整体吞吐量的主要瓶颈。
3.5 性能结论
结论:在当前阶梯并发压测场景下,ShopXO 系统核心接口均能正常响应,无异常请求,系统稳定性达标。但整体平均响应时间约 3.9 秒,首页接口响应偏慢(平均近 5 秒),存在一定的性能优化空间。
四、接口测试
4.1 测试工具与框架
本次接口自动化测试基于 Python 生态搭建,核心技术栈如下:
| 工具 / 库 | 版本 | 用途 |
|---|---|---|
| pytest | 8.3.2 | 测试框架,用例管理与执行 |
| requests | 2.31.0 | HTTP 请求库,发送接口请求 |
| jsonschema | 4.23.0 | 响应数据结构校验 |
| PyYAML | 6.0.1 | 测试数据读写 |
| allure-pytest | 2.13.5 | 测试报告生成 |
| pytest-order | 1.3.0 | 用例执行顺序控制 |
4.2 框架搭建
4.2.1 项目目录结构
ShopXO_api_auto_test/ ├── cases/ # 测试用例(6个测试文件) │ ├── __init__.py │ ├── test_login.py │ ├── test_home_page.py │ ├── test_goods_list.py │ ├── test_goods_detail.py │ └── test_cart.py ├── utils/ # 工具类 │ ├── requests_utils.py │ ├── logging_utils.py │ └── yaml_utils.py ├── data/ # 测试数据 ├── logs/ # 三级日志(all/info/err) ├── allure-report/ # Allure报告 ├── allure-results/ # Allure结果数据 ├── conftest.py # 全局Fixture └── pytest.ini # pytest配置4.2.2 核心模块设计
(1)请求封装模块(requests_utils.py)
封装统一的请求入口,基于requests.Session()实现 Cookie 自动管理,同时集成日志记录:
import requests from utils.logging_utils import Logger host = "http://121.43.128.122/?" class Requests: def __init__(self): # 创建 Session 对象,自动保存和携带 Cookie self.session = requests.Session() self.log = Logger.getLogger() def get(self, url, **kwargs): self.log.info(f"准备发起的GET请求,utl: {url}") self.log.info(f"接口信息:{kwargs}") r = self.session.get(url, **kwargs) self.log.info(f"接口返回状态码为:{r.status_code}") self.log.info(f"接口返回信息: {r.text}") return r def post(self, url, **kwargs): self.log.info(f"准备发起的POST请求,url: {url}") self.log.info(f"接口信息:{kwargs}") r = self.session.post(url, **kwargs) self.log.info(f"接口返回状态码为:{r.status_code}") self.log.info(f"接口返回信息: {r.text}") return r(2)日志模块(logging_utils.py)
实现日志分级存储,通过自定义 Filter 精确控制各级别日志的输出:
import logging import os import time class info_filter(logging.Filter): def filter(self, record): return record.levelno == logging.INFO class err_filter(logging.Filter): def filter(self, record): return record.levelno == logging.ERROR class Logger: # 创建日志类 logger = None # 使用类方法 把日志记录器挂在Logger类上 全局共同使用 @classmethod def getLogger(cls): if cls.logger is None: # 创建日志记录器对象 cls.logger = logging.getLogger("ShopXO") # 设置日志最低等级为DEBUG cls.logger.setLevel(level=logging.DEBUG) """ 文件存放格式: logs 2026-9-29.log 2026-9-29_info.log 2026-9-29_err.log """ # 创建logs文件夹 Logs_Path = "./logs/" if not os.path.exists(Logs_Path): os.mkdir(Logs_Path) # 生成实时时间 now = time.strftime("%Y-%m-%d") # 文件名称 以及文件存放路径 log_name = Logs_Path + now + ".log" log_info_name = Logs_Path + now + "_info.log" log_err_name = Logs_Path + now + "_err.log" # 创建一个日志写入器,设置日志文件写入位置 all_handler = logging.FileHandler(filename=log_name, encoding="utf-8") info_handler = logging.FileHandler(filename=log_info_name, encoding="utf-8") err_handler = logging.FileHandler(filename=log_err_name, encoding="utf-8") # 创建日志格式器,设置日志输出格式 formatter = logging.Formatter( "%(asctime)s %(levelname)s [%(name)s] [%(filename)s (%(funcName)s:%(lineno)d)] - %(message)s" ) # 将格式器添加到写入器中 all_handler.setFormatter(formatter) info_handler.setFormatter(formatter) err_handler.setFormatter(formatter) # 添加日志过滤器 精确捕捉指定日志级别的文件 info_handler.addFilter(info_filter()) err_handler.addFilter(err_filter()) # 添加日志写入器到日志记录器中 # 日志记录器使用日志写入器处理日志信息 cls.logger.addHandler(all_handler) cls.logger.addHandler(info_handler) cls.logger.addHandler(err_handler) return cls.logger(3)全局 Fixture 配置(conftest.py)
通过scope="session"的 fixture 实现全局共享一个 Requests 实例,并自动登录获取登录态:
import pytest from utils.requests_utils import Requests @pytest.fixture(scope="session") def req(): req = Requests() return req4.3 测试用例示例
4.3.1 登录接口(参数化 + 异常场景)
登录接口采用参数化方式覆盖正常登录和多种异常场景:
import pytest from jsonschema.validators import validate from utils.requests_utils import host @pytest.mark.order(1) class TestLogin: url = host + "s=user/login&system_type=default" schema = { "type": "object", "properties": { "msg": { "type": "string" }, "code": { "type": "number" }, "data": { "type": ["object", "string"], "properties": { "body_html": { "type": "string" }, "user_id": { "type": "string" } }, "required": [ "body_html", "user_id" ] } }, "required": [ "msg", "code", "data" ] } @pytest.mark.parametrize("login", [ { "accounts": ***, "pwd": ******* } ]) def test_login_suc(self, login, req): params = { "accounts": login["accounts"], "pwd": login["pwd"], "type": "username" } r = req.post(url=self.url, params=params) validate(instance=r.json(), schema=self.schema) assert r.json()["code"] == 0 assert r.json()["msg"] == "登录成功" assert r.json()["data"]["user_id"] == "20" @pytest.mark.parametrize("login", [ { "accounts": 111, "pwd": 1111111111, "type": "username" }, { "accounts": 1111111111111, "pwd": 1111111111, "type": "username" }, { "accounts": "", "pwd": 1111111111, "type": "username" }, { "accounts": 11, "pwd": "", "type": "username" }, { "accounts": 11, "pwd": 1111111111, "type": "" }, { "accounts": 11, "pwd": 1111111111, "type": "qe2313##" } ]) def test_login_fail(self, login, req): params = { "accounts": login["accounts"], "pwd": login["pwd"], } r = req.post(url=self.url, params=params) validate(instance=r.json(), schema=self.schema) assert r.json()["data"] == "" assert r.json()["code"] != 04.3.2 商品列表接口(分页参数化)
商品列表接口通过参数化验证不同分页参数的正确性:
import pytest from jsonschema.validators import validate from utils.requests_utils import host class Test_goods_list: url = host + "s=category/datalist&system_type=default" schema = { "type": "object", "properties": { "msg": { "type": "string" }, "code": { "type": "number" }, "data": { "type": "object", "properties": { "page_start": { "type": "number" }, "page_size": { "type": "number" }, "page": { "type": "number" }, "page_total": { "type": "number" }, "total": { "type": "number" }, "data": { "type": "string" } }, "required": [ "page_start", "page_size", "page", "page_total", "total", "data" ] } }, "required": [ "msg", "code", "data" ] } @pytest.mark.parametrize("data", [ { "category_id": 2, "page": 1, "page_size": 10 } ]) def test_right_data(self, req, data): params = { "category_id": data["category_id"], "page": data["page"], "page_size": data["page_size"] } r = req.post(url=self.url, params=params) validate(instance=r.json(), schema=self.schema) assert r.json()["msg"] == "处理成功" assert r.json()["code"] == 0 assert r.json()["data"]["page"] == 1 @pytest.mark.parametrize("page, page_size", [ (1, 10), (2, 10), (1, 20), (3, 5), ]) # 商品列表分页 def test_goods_list_pagination(self, req, page, page_size): params = { "category_id": 2, "page": page, "page_size": page_size } r = req.get(url=self.url, params=params) validate(instance=r.json(), schema=self.schema) assert r.json()["code"] == 0 def test_goods_list_false_id(self, req): params = { "category_id": 66666666, "page": 2, "page_size": 10 } r = req.get(url=self.url, params=params) validate(instance=r.json(), schema=self.schema) assert r.json()["msg"] == "没有相关数据" assert r.json()["code"] == -1 def test_goods_list_false_page(self, req): params = { "category_id": 2, "page": 6666666666666, "page_size": 10 } r = req.get(url=self.url, params=params) validate(instance=r.json(), schema=self.schema) assert r.json()["data"]["page_total"] == 1 # 总共就1页 assert r.json()["code"] == 0 def test_goods_list_false_page_size(self, req): params = { "category_id": 2, "page": 2, "page_size": 666666666 } r = req.get(url=self.url, params=params) validate(instance=r.json(), schema=self.schema) assert r.json()["msg"] == "处理成功" assert r.json()["code"] == 04.3.3 购物车接口(Base64 编码 + 规格参数)
购物车加入接口的商品数据需要进行 Base64 编码后传输,且需包含完整的商品规格信息:
import base64 import json import pytest from jsonschema.validators import validate from utils.requests_utils import host class Test_cart: url = host + "s=cart/save&system_type=default" schema = { "type": "object", "properties": { "msg": { "type": "string" }, "code": { "type": "number" }, "data": { "type": ["object","string"], "properties": { "total_price": { "type": "string" }, "buy_number": { "type": "string" } }, "required": [ "total_price", "buy_number" ] } }, "required": [ "msg", "code", "data" ] } def test_cart_suc(self, req): goods_list = [{ "goods_id": "11", "stock": 1, "spec": [ {"type": "尺码", "value": "L"} ] }] goods_data = base64.b64encode(json.dumps(goods_list).encode("utf-8")).decode("utf-8") data = {"goods_data": goods_data} r = req.post(url=self.url, data=data) validate(instance=r.json(), schema=self.schema) assert r.json()["msg"] == "加入成功" assert r.json()["code"] == 0 def test_cart_wrong_id(self, req): goods_list = [{ "goods_id": "999999", "stock": 1, "spec": [ {"type": "尺码", "value": "L"} ] }] goods_data = base64.b64encode(json.dumps(goods_list).encode("utf-8")).decode("utf-8") data = {"goods_data": goods_data} r = req.post(url=self.url, data=data) validate(instance=r.json(), schema=self.schema) assert r.json()["msg"] == "1、商品不存在或已删除" assert r.json()["code"] == -2 assert r.json()["data"] == "" def test_cart_wrong_stock(self, req): goods_list = [{ "goods_id": "11", "stock": 999999, "spec": [ {"type": "尺码", "value": "L"} ] }] goods_data = base64.b64encode(json.dumps(goods_list).encode("utf-8")).decode("utf-8") data = {"goods_data": goods_data} r = req.post(url=self.url, data=data) validate(instance=r.json(), schema=self.schema) assert r.json()["code"] == -1 assert r.json()["data"] == ""4.5 运行结果
本次接口自动化测试共覆盖 5 个核心模块,执行 20+ 个测试用例,整体通过率 100% 。测试结果统计如下:
| 模块 | 用例数 | 通过 | 失败 | 通过率 |
|---|---|---|---|---|
| 用户登录 | 7 | 7 | 0 | 100% |
| 首页 / UEditor 配置 | 5 | 5 | 0 | 100% |
| 商品列表 | 8 | 8 | 0 | 100% |
| 商品详情 | 5 | 5 | 0 | 100% |
| 购物车 | 3 | 3 | 0 | 100% |
| 合计 | 28 | 28 | 0 | 100% |
测试结论:ShopXO 系统核心接口功能正常,异常处理较为完善,登录、商品浏览等主流程接口稳定性达标。购物车模块因规格参数复杂未完全覆盖,后续可补充完善。测试框架基于 pytest + requests 搭建,具备良好的可扩展性和可维护性。
Allure 测试报告总览:
各模块用例执行详情: