
目录一、功能测试1.1 测试用例总汇表1.2 代表性用例展示1.3 缺陷统计1.4 重点缺陷描述二、 UI自动化测试2.1 技术选型2.2 项目结构2.3 核心代码实现1Driver 工具类封装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 测试用例总汇表模块用例数成功失败阻塞备注首页4310图片加载失败商品7700搜索3300用户8710购物车5500订单4211无支付方式后台登录与首页6600后台商品管理5500后台订单管理3201前后台数据一致性4202安全4400兼容性2200后台用户管理2200合计575034成功率 87.7%1.2 代表性用例展示正确账号密码登录登陆成功加入购物车操作成功后台订单管理1.3 缺陷统计严重程度数量严重1一般3轻微1合计51.4 重点缺陷描述BUG004严重订单结算页无支付方式无法提交订单BUG002/003一般手机 / 邮箱注册验证码发送失败BUG001轻微首页图片加载失败 → 贴截图二、 UI自动化测试2.1 技术选型UI 自动化测试采用Python Selenium pytest技术栈SeleniumWeb 应用自动化测试框架支持多浏览器驱动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 核心代码实现1Driver 工具类封装在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(serviceService(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(scopesession) # 整个测试会话只启动一次浏览器 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.driverWebDriver 实例而非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、数据库等多种服务进行压力测试能够模拟多用户并发访问并通过聚合报告、响应时间趋势图、吞吐量趋势图等监听器全面采集性能指标。测试计划中使用了jpgc - Stepping Thread Group阶梯线程组来模拟用户量逐步递增的并发场景更贴近真实用户逐步涌入系统的访问模式。3.2 测试场景设计针对 ShopXO 电商系统的核心高频页面选取以下三个接口进行并发压测序号接口名称说明1首页系统首页加载包含商品推荐、轮播图等模块2商品列表商品分类列表页包含分页、筛选等数据查询3商品详情单个商品详情页包含商品信息、规格、图片等加载同时配置了以下监听器用于结果采集与分析jpgc - Active Threads Over Time监控活动线程数随时间的变化jpgc - Response Times Over Time监控响应时间随时间的变化趋势jpgc - Transactions per Second监控每秒事务数吞吐量变化聚合报告汇总各接口的核心性能指标3.3 测试结果本次压测共发起357个请求全部请求均成功异常率为0.00%。聚合报告核心数据如下接口名称样本数平均响应时间 (ms)中位数 (ms)90% 百分位 (ms)95% 百分位 (ms)99% 百分位 (ms)最小值 (ms)最大值 (ms)异常率吞吐量 (次 / 秒)首页1274920419482201102213644796170590.00%1.7商品列表120349329636541761310152552156280.00%1.6商品详情1103149301250545495628066264440.00%1.5总体357389533976502822012074552170590.00%4.73.4 结果分析1系统稳定性三个接口的异常率均为 0.00%说明在当前并发压力下系统能够稳定处理所有请求未出现服务不可用或报错的情况系统稳定性良好。2响应时间分析总体平均响应时间为3895ms约 3.9 秒中位数为3397ms说明半数以上请求能在 3.4 秒内完成。首页响应时间最长平均 4920ms95% 百分位达到 11022ms最大响应时间 17059ms。原因是首页加载内容较多包含轮播图、推荐商品、分类导航等多个模块数据查询和资源加载开销较大。商品列表平均响应时间 3493ms表现居中。商品详情响应时间最短平均 3149ms且 99% 百分位仅 6280ms响应时间波动最小接口性能最稳定。3吞吐量分析系统总体吞吐量为4.7 次 / 秒其中首页 1.7 次 / 秒、商品列表 1.6 次 / 秒、商品详情 1.5 次 / 秒三个接口吞吐量分布较为均衡。首页由于响应时间较长单位时间内完成的请求数相对受限是影响整体吞吐量的主要瓶颈。3.5 性能结论结论在当前阶梯并发压测场景下ShopXO 系统核心接口均能正常响应无异常请求系统稳定性达标。但整体平均响应时间约 3.9 秒首页接口响应偏慢平均近 5 秒存在一定的性能优化空间。四、接口测试4.1 测试工具与框架本次接口自动化测试基于 Python 生态搭建核心技术栈如下工具 / 库版本用途pytest8.3.2测试框架用例管理与执行requests2.31.0HTTP 请求库发送接口请求jsonschema4.23.0响应数据结构校验PyYAML6.0.1测试数据读写allure-pytest2.13.5测试报告生成pytest-order1.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 r2日志模块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(levellogging.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(filenamelog_name, encodingutf-8) info_handler logging.FileHandler(filenamelog_info_name, encodingutf-8) err_handler logging.FileHandler(filenamelog_err_name, encodingutf-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.logger3全局 Fixture 配置conftest.py通过scopesession的 fixture 实现全局共享一个 Requests 实例并自动登录获取登录态import pytest from utils.requests_utils import Requests pytest.fixture(scopesession) 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 suser/loginsystem_typedefault 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(urlself.url, paramsparams) validate(instancer.json(), schemaself.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(urlself.url, paramsparams) validate(instancer.json(), schemaself.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 scategory/datalistsystem_typedefault 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(urlself.url, paramsparams) validate(instancer.json(), schemaself.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(urlself.url, paramsparams) validate(instancer.json(), schemaself.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(urlself.url, paramsparams) validate(instancer.json(), schemaself.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(urlself.url, paramsparams) validate(instancer.json(), schemaself.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(urlself.url, paramsparams) validate(instancer.json(), schemaself.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 scart/savesystem_typedefault 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(urlself.url, datadata) validate(instancer.json(), schemaself.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(urlself.url, datadata) validate(instancer.json(), schemaself.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(urlself.url, datadata) validate(instancer.json(), schemaself.schema) assert r.json()[code] -1 assert r.json()[data] 4.5 运行结果本次接口自动化测试共覆盖 5 个核心模块执行 20 个测试用例整体通过率 100% 。测试结果统计如下模块用例数通过失败通过率用户登录770100%首页 / UEditor 配置550100%商品列表880100%商品详情550100%购物车330100%合计28280100%测试结论ShopXO 系统核心接口功能正常异常处理较为完善登录、商品浏览等主流程接口稳定性达标。购物车模块因规格参数复杂未完全覆盖后续可补充完善。测试框架基于 pytest requests 搭建具备良好的可扩展性和可维护性。Allure 测试报告总览各模块用例执行详情本次代码存放的gitee仓库https://gitee.com/sddsfsdf455/shop-xo_auto_test.git