
ShopXO 商城测试实战报告项目背景本次测试对象是 ShopXO 商城系统。项目按照真实电商系统的测试思路进行拆分重点从 Web UI 自动化、接口自动化和低并发性能测试三个方向开展验证。本文只记录本轮实际执行并保留证据的测试内容不将未覆盖的下单、支付、后台新增、编辑、删除等功能写入测试结论。为保护测试环境安全文中的真实域名、后台入口、账号信息、Cookie/Session 及关键请求参数均了做脱敏处理。最终结果如下测试类型工具实际结果Web UI 自动化Python pytest Selenium53 条用例接口自动化Postman requests pytest13 条用例全量回归13 passed性能测试JMeter首页、搜索低并发测试错误率 0%一、先用 XMind 重新梳理测试用例在写自动化脚本之前我先用 XMind 重新梳理了测试范围。这样做的目的不是为了画图好看而是为了避免一上来就写代码最后发现测试范围混乱、用例重复或者漏掉权限边界。XMind 中主要拆了四层用户端页面首页、注册、登录页、搜索、分类、商品详情权限边界游客访问个人中心、订单、购物车的跳转管理端只读模块后台商品、用户、分类、订单列表展示接口与性能接口请求契约、重定向、响应结构、JMeter 首页和搜索基线。如果博客发布时只能保留一张设计图建议优先保留 XMind 测试范围总览图因为它能够体现项目并非简单页面点击而是先完成测试范围拆分和用例设计再落地到自动化执行。本轮自动化推进顺序可以概括为XMind 测试设计 → 页面元素定位 → Page 层封装 → pytest 单模块调试 → 全量回归 → Allure 报告二、Web UI 自动化测试UI 自动化使用Python pytest Selenium完成最终统计到 53 条 Web UI 自动化用例覆盖当前测试环境中的核心只读流程和权限边界。实际覆盖内容包括首页展示登录页契约检查注册账号、手机、邮箱入口注册字段边界和格式校验搜索结果页分类页商品详情页不存在商品编号游客访问个人中心、订单、购物车的重定向后台商品、用户、分类、订单等只读列表展示。为了让测试范围不只停留在“跑了多少条用例”我把 UI 自动化覆盖点拆成了用户端业务、注册登录、后台只读三个方向。用户端主要验证首页、搜索、分类、详情和游客权限跳转1. 注册字段为什么要单独测注册页存在账号、手机、邮箱三个入口。三个入口看起来相似但字段规则不同注册入口本轮验证重点账号注册用户名字符类型、用户名长度、密码长度手机注册手机号格式、密码长度、验证码字段契约邮箱注册邮箱格式、密码长度、验证码字段契约验证码依赖短信或邮箱通道所以本轮没有写成“真实注册成功”。我只验证当前环境能稳定检查的内容输入框、字段属性、前端校验状态和页面提示。注册和登录相关用例按入口、字段契约和动态校验结果继续拆分2. CSS Selector 注意点注册页不同 Tab 中可能存在相同字段名例如nameaccounts。如果直接写driver.find_element(By.CSS_SELECTOR,input[nameaccounts])可能会定位到隐藏面板中的输入框。因此我会先限定表单区域再定位字段username_inputdriver.find_element(By.CSS_SELECTOR,form.form-validation-username input[nameaccounts])这个选择器表达的是“账号注册表单里的账号输入框”不是页面上任意一个accounts字段。写入代码前我会在浏览器 Console 中确认唯一性document.querySelectorAll(form.form-validation-username input[nameaccounts]).length返回1才说明当前选择器在页面中唯一。在自动化代码中这类选择器会统一封装到 PageObject 方法里。例如注册页的账号输入框不会散落在测试用例中直接写而是通过GetUsernameInput()暴露给用例调用。这样后续页面结构变化时只需要维护页面对象里的定位表达式测试用例本身不用大面积修改。3. 前端校验状态怎么断言注册字段校验不是只判断页面有没有弹窗。页面输入非法数据后前端会给输入框增加校验状态 class例如am-field-valid输入通过校验am-field-error输入未通过校验。所以断言时不比较完整 class 字符串而是检查关键 tokenactual_classemail_input.get_attribute(class)assertexpected_tokeninactual_class这样可以避免基础样式变化导致误判。4. 游客权限跳转怎么测个人中心、订单、购物车属于登录后页面。游客直接访问时系统应该跳转到登录页。本轮测试覆盖了场景预期游客访问个人中心跳转到用户登录页游客访问订单页跳转到用户登录页游客访问购物车跳转到用户登录页未登录访问后台商品列表跳转到后台登录页这类用例的价值在于验证权限边界而不是简单判断页面能不能打开。后台测试只做已登录后的只读列表回归不做新增、编辑、删除等写操作后台登录成功后本轮 UI 自动化还覆盖了商品、用户、分类、订单等只读列表页。下面是后台商品列表页的真实截图截图中只保留页面内容区不展示浏览器地址栏和后台入口路径。三、接口自动化测试接口测试先使用 Postman 复现请求再用requests pytest固化为自动化脚本。为保护测试环境安全本文中接口示例统一使用脱敏占位域名http://82.153.3.118不展示真实服务地址。最终共编写 13 条接口自动化用例全量回归结果为13 passed接口地址和返回结果按实际请求整理如下。表格中的域名统一使用http://82.153.3.118作为脱敏示例后台入口文件名也做了脱敏处理实际自动化断言以项目配置中的真实地址为准。检查项请求方式请求地址相对路径HTTP 结果说明用户端首页GET/200首页 HTML 页面可访问正文包含 ShopXO 关键字用户登录页GET/?suser/loginInfo.html200登录页可访问页面包含“用户登录 / 欢迎登录”商品分类首页GET/?scategory/index.html200分类首页可访问页面包含“商品分类”数码办公分类页GET/?ssearch/index/cid/1.html200分类结果页可访问页面包含“数码办公”商品详情页GET/?sgoods/index/id/3.html200商品详情页可访问页面包含实际商品标题不存在商品 IDGET/?sgoods/index/id/999999.html200不返回 404而是返回业务提示“商品不存在或已删除”搜索手机关键词POST/?ssearch/index.html302 → 200提交搜索后先重定向再访问最终搜索结果页搜索特殊字符关键词POST/?ssearch/index.html302 → 200搜索手机电脑页面中 HTML 实体解码后能还原关键词搜索空关键词POST/?ssearch/index.html200空关键词不会跳转直接返回默认商品搜索页搜索不存在商品POST/?ssearch/index.html302 → 200最终结果页保留搜索词并展示“没有相关数据”商品二维码数据POST/?sgoods/qrcodedatasystem_typedefault200JSON 响应使用 jsonschema 校验结构code0商品评论分页POST/?sgoods/commentssystem_typedefault200JSON 响应校验评论分页字段和code0后台商品列表未登录访问GET/admin-entry.php?sgoods/index.html200后台入口已脱敏未登录时返回 HTML/JS并跳转到后台登录页1. 接口断言不能只看状态码如果只写assertresponse.status_code200并不能说明业务一定正确。因为有些页面返回 200 也可能是错误页有些请求本来就应该返回 302。本项目中我按响应类型设计断言响应类型断言重点HTML 页面状态码、Content-Type、页面关键字搜索提交302、Location、最终结果页权限重定向302、登录页路径JSON/辅助接口状态码、响应结构、必要字段URL 编码场景对response.url解码后再断言整体断言思路可以概括为先判断协议层结果 → 再判断响应类型 → 再判断业务关键字段/关键页面内容 → 最后结合重定向地址或解码后的 URL 做业务断言2. URL Encode 问题商品详情接口中浏览器 Network 看到的是goods/index/id/3.html但requests获取到的最终 URL 可能是goods%2Findex%2Fid%2F3.html这不是路由变化而是 query 参数被 URL Encode 了。最终通过urllib.parse.unquote解码后再断言fromurllib.parseimportunquote final_urlunquote(response.url)assertgoods/index/id/3.htmlinfinal_url这个问题让我意识到接口自动化不能只关注状态码还要理解请求参数编码、响应对象和业务路径之间的区别。3. 特殊字符搜索搜索特殊字符时我使用过手机电脑页面中可能显示为手机amp;电脑这是 HTML 实体转义不代表输入丢失。断言时不能直接把原始字符串和整段 HTML 硬匹配而要理解 URL Encode 和 HTML Entity 是不同层面的编码。四、JMeter 性能测试功能验证通过并不代表访问稳定因此我使用 JMeter 做了低并发性能基线测试。需要说明的是本项目部署在小规格云服务器环境中服务器配置约为 2 核 2G更适合做功能验证和低并发基线观察不适合直接进行高并发压测或容量上限评估。因此本轮性能测试的目标不是压垮系统而是在可控压力下观察响应时间、吞吐量、错误率和长尾请求。1. JMeter 基线场景设计本轮选择 7 个公开只读页面做低并发基线用户端首页商品搜索商品分类商品详情无库存商品详情管理端登录页管理端后台首页的未登录访问。测试计划设置如下线程数3 循环次数5 请求页面7 理论请求数3 × 5 × 7 105 连接超时10000 ms 响应超时5000 ms我把并发控制在 3是因为这轮目标是建立响应时间基线和发现明显稳定性问题不是直接把公共测试环境压垮。这个结果也不能被解释成系统容量上限。2. JMeter 结果总览以下数据取自本轮重新执行生成的 JMeter Dashboard。为了避免对公共测试环境造成过高压力本轮只做低并发基线验证共执行 105 个请求整体结果如下指标结果成功105失败0错误率0.00%平均响应时间2584.94 ms中位数1747.00 msP907054.00 msP9510109.60 msP9921287.22 ms最大响应时间21516 ms吞吐量0.98 次/秒本轮 105 个请求全部成功说明在 3 个并发用户的低并发基线下没有出现请求失败。但只看错误率还不够响应时间仍然存在长尾P95 为 10109.60 msP99 为 21287.22 ms最大响应时间达到 21516 ms。也就是说绝大多数请求可以正常返回但个别页面在小规格服务器上仍会出现明显慢响应。因此我把 JMeter Dashboard 的总览、统计表和百分位曲线都保留下来这几张图我主要看以下指标Requests Summary绿色 PASS 为 100%表示本轮没有 HTTP 失败和断言失败如果这里出现红色 FAIL就要继续看 Errors 表和 JTL 明细。#Samples请求样本数。本轮总数 105来自3 个线程 × 5 次循环 × 7 个采样器。FAIL和Error %失败数和错误率。本轮为 0说明低并发下可用性通过。Average平均响应时间。本轮总平均为 2584.94 ms但平均值容易被极慢或极快请求影响所以不能只看平均值。Median中位数。本轮为 1747 ms表示有一半请求在 1.747 秒以内完成比平均值更能反映“多数请求”的体感。90th pct / 95th pct / 99th pct百分位响应时间。本轮 P95 为 10109.60 msP99 为 21287.22 ms说明少量请求明显拖慢属于长尾响应。Max最大响应时间。本轮最大 21516 ms出现在用户端首页说明首页在小规格服务器上偶发慢响应比较明显。Throughput吞吐量。本轮总吞吐量约 0.98 次/秒。由于本轮是低并发基线不是阶梯压测所以这个值只能说明当前场景下的处理速率不能当作系统最大吞吐能力。APDEXJMeter 默认以 500 ms 作为满意阈值、1.5 秒作为容忍阈值。本轮 Total APDEX 为 0.352说明按这个默认阈值看用户端页面响应偏慢。但这里的阈值不是业务方正式 SLA只能作为体验参考。百分位曲线中用户端首页曲线在 80% 之后明显抬升说明首页不是每次都慢而是存在少量长耗时样本这类问题通常需要结合服务器 CPU、内存、数据库慢查询、Nginx 日志继续排查而不能只凭 JMeter 客户端结果直接判断是哪一层慢。3. 各页面响应时间分析按采样器拆开看本轮慢响应主要集中在用户端首页、商品搜索和商品详情这些页面。管理端未登录访问响应很快是因为未登录状态下直接返回登录页或重定向结果没有真正进入后台业务查询。采样器请求数失败数错误率平均响应P95最大响应用户端首页1500.00%5295.47 ms21516.00 ms21516 ms商品搜索-手机1500.00%3400.67 ms11203.00 ms11203 ms商品详情-商品121500.00%3455.80 ms9838.00 ms9838 ms商品分类-数码办公1500.00%2935.40 ms8456.00 ms8456 ms用户端无库存商品-商品2131500.00%2763.93 ms7571.00 ms7571 ms管理端登录页未登录1500.00%203.80 ms735.00 ms735 ms管理端后台首页未登录1500.00%39.53 ms44.00 ms44 ms这轮没有出现Read timed out或 HTTP 错误所以不能为了让问题显得严重而编造失败结论。更合理的结论是低并发下功能可用、请求成功率达标但用户端首页和商品相关页面存在长尾响应后续需要结合服务器 CPU、内存、数据库慢查询和 Web 服务日志继续分析。4. 性能测试优化思路后续优化时我会先解决可解释性再提高压力将首页、搜索、分类、详情拆成单独场景重复运行确认慢点是否稳定复现同时采集服务器 CPU、内存、网络、磁盘、Web 容器线程和数据库指标明确性能目标例如错误率小于 1%、P95 小于 3 秒再判断是否达标增加阶梯并发而不是一次性把线程数调得很高优化后使用相同 JMX 和测试数据复测比较错误率、P95 和吞吐量变化。5. 单场景补充验证除 7 页面综合基线外我还重新执行了首页和搜索场景的单场景测试用于观察单个入口在固定线程下的响应情况。单场景请求数较少不能单独代表系统容量但可以帮助对比“首页页面渲染”和“搜索提交入口”的耗时差异场景并发/线程样本数平均响应P95最大响应错误率结论首页访问5 线程53552.60 ms5202.00 ms5202 ms0.00%低并发下能成功返回但页面响应偏慢首页访问10 线程105209.90 ms9610.00 ms9610 ms0.00%线程数增加后平均耗时和最大耗时上升搜索提交10 线程1085.30 ms134.00 ms134 ms0.00%搜索提交入口响应很快这里需要注意单场景里的“搜索提交”与综合基线里的“商品搜索结果页”不是完全同一个成本搜索提交入口更偏轻量请求返回快而搜索结果页需要展示商品列表和页面内容所以综合基线中商品搜索结果页平均响应达到 3400.67 ms。复盘时不能把这两个数字直接当成同一个接口对比。JMeter 聚合报告中的响应时间单位是ms也就是毫秒。例如85.30 ms大约是0.085 秒。另外线程组中设置的线程数和循环次数不等于右上角运行时间一定固定。JMeter 还需要线程启动、请求发送、响应等待、结果收集、HTML 报告生成和线程结束所以实际运行耗时可能略大于理论请求耗时。五、项目中遇到的真实问题1.RequestUtil.post()调用方式错误商品评论接口一开始写成RequestUtil.post(...)运行时报错TypeError: RequestUtil.post() missing 1 required positional argument: self原因是post是实例方法不能直接用类名调用。修复后改成RequestUtil().post(...)2.response.url中出现%2F商品详情断言中直接判断goods/index/id/3.html会失败因为requests对 query 参数进行了 URL Encode。解决方式是先unquote解码再做业务断言。3. 搜索结果页出现 502 时怎么排查搜索提交后会先返回重定向地址再请求最终结果页。排查时我会打印print(response.status_code)print(response.headers)print(response.url)print(response.text[:500])这样可以判断问题出在提交请求、重定向地址还是最终结果页。4. Allure 报告生成本轮 UI 自动化执行完成后通过allure-pytest生成allure-results再使用 Allure CLI 生成 HTML 报告。报告中可以直观看到用例总数、通过率和各测试套件分布。生成命令如下allure generate.\reports\allure-results-o.\reports\allure-report--clean即可生成可在浏览器中查看的 Allure 报告。六、最终结果本轮 ShopXO 商城测试最终结果如下Web UI 自动化53 条用例 接口自动化13 条用例全量回归 13 passed 性能测试首页 5/10 线程错误率 0%首页 10 线程平均响应约 5266 ms搜索 10 线程平均响应约 81 ms对于未覆盖的下单、支付、后台新增、编辑、删除等高风险写操作本文不会将其写入已完成测试结论。测试结论只基于本轮已执行、可复现、可留存证据的范围。七、项目总结本次 ShopXO 商城测试实践从测试设计、自动化执行、接口校验、性能基线和问题复盘五个方面展开。项目先通过 XMind 梳理测试范围再使用 Selenium、requests 和 JMeter 分别完成 UI、接口和性能层面的验证最后通过 pytest、Allure 和测试记录沉淀执行结果。从结果来看本轮测试完成了 53 条 Web UI 自动化用例、13 条接口自动化用例以及首页和搜索场景的低并发性能验证从过程来看项目中也覆盖了 CSS Selector 稳定定位、字段边界校验、游客权限重定向、URL 编码处理、特殊字符搜索、接口重定向断言和测试报告生成等实践内容。后续如果继续扩展可以在隔离测试数据和回滚机制完善后补充下单、支付、后台新增、编辑、删除等写操作场景并结合服务器监控进一步分析性能瓶颈。八、项目来源说明本次测试实践基于开源商城项目 ShopXO 搭建测试环境并开展开源项目地址https://gitcode.com/zongzhige/shopxo。