1. 为什么拿不爱听书当练手项目选型逻辑与测试范围拆解软件测试项目实战这四个字在简历上出现的频率极高但真正能扛住面试官追问你测了什么、怎么测的、发现了什么问题的人并不多。我最近把这套【不爱听书】的测试教程和源码从头跑了一遍又在原有基础上补了接口自动化和性能压测两块内容前后折腾了小一个月。这篇文章不打算复述教程里写好的操作步骤而是把我读源码时的心得、搭环境时踩的坑、以及如果重做一遍我会怎么安排完整摊开讲。它适合两类人一类是刚学完测试理论、急需一个完整项目来串联知识点的入门者另一类是工作两三年、想从纯功能测试往自动化方向挪一挪的同行。先把被测对象说清楚。不爱听书是一个在线听书类应用业务上属于典型的内容型 C 端产品包含书籍分类浏览、搜索、详情页、播放器、书架收藏、收听历史、评论、会员权益这几大块。工程上是前后端分离架构前端有 Web 端页面后端提供 REST 风格的接口数据落库。这套结构的价值在于它足够小一个人就能跑通全链路又足够真实登录态、分页、缓存、并发、文件上传这些实际工作中的坑它一个不少。教程配套的源码是可以直接跑起来的完整工程这一点比那些只给你一堆接口文档、后端却要自己造假的伪项目强太多。1.1 练手项目怎么挑才不浪费时间我见过太多人卡在学完理论没项目练这一步然后随手找了个增删改查的 Demo 当项目写进简历。问题在于技术含量太低的东西你自己讲着都心虚。判断一个练手项目值不值得投入我一般看三条业务链路是否完整、技术栈是否贴近主流、是否有真实的可测点。不爱听书在这三条上都过关。业务上从打开 App 到听完一本书并留下记录是一条完整闭环中间穿插登录、搜索、收藏、播放进度上报等多个环节技术上是前后端分离 数据库 缓存跟当下大部分公司的实际项目结构一致可测点上光是播放器这一块就能扯出断点续播、倍速切换、后台播放、网络切换恢复等一堆场景。这些场景在面试里都是能展开聊的素材比我测了一个登录功能有说服力得多。反过来讲如果某个项目的核心逻辑是把数据从 A 表搬到 B 表那它作为测试练手项目的价值就很有限因为你没法设计出有深度的用例也谈不上什么测试策略。选项目这一步花两天时间想清楚比后面瞎测三周都值。1.2 先画业务链路图再谈用例设计拿到项目之后我的习惯是先在纸上画出核心业务链路而不是急着打开 Postman 发请求。这一步很多人跳过结果就是用例写成了一盘散沙东一个登录、西一个搜索彼此之间没有关联。以不爱听书为例我画出来的主链路是游客浏览首页 - 点击书籍进入详情 - 触发登录/注册 - 登录后加入书架 - 进入播放页开始播放 - 播放过程中上报进度 - 退出后再次进入验证续播 - 收听历史列表校验。这条链路上每一个箭头都是一次状态流转也都是一次潜在的缺陷点。比如登录后加入书架这一步如果用户的登录态是通过 token 维持的那么 token 过期时点击收藏会发生什么是静默失败还是弹出提示这类问题只有把链路串起来才想得到。链路图画完之后再把它拆成模块账号模块、书籍模块、播放模块、书架模块、搜索模块、评论模块。模块是横向切分链路是纵向贯穿两者交叉的位置就是用例最密集的地方。我在实际整理时用的是双色标注法横线画模块、竖线画链路交叉点打圈最后这张图直接决定了后面用例的优先级排序。1.3 测试范围与优先级怎么划一个完整的听书应用理论上可以测到天荒地老。实际做项目时你必须做减法否则时间全耗在边角料上。我给自己定的范围是核心链路必测、异常分支重点测、兼容性抽样测、性能做基线。优先级排序我用的是一张简单的矩阵表横轴是业务重要度纵轴是出错概率四个象限分别对应不同的投入策略业务重要度 / 出错概率高概率低概率高重要度用例写细、反复回归如登录、播放、支付类用例覆盖即可如首页推荐位展示低重要度抽查为主如个人资料修改不投入如关于我们页面文案这张表的实际用法是每次执行回归测试前先看改动范围落在哪个象限再决定这一轮要跑哪些用例。比如后端只改了播放进度上报接口那就重点跑高重要度高概率这一格其余按抽样比例跑。这样一轮回归能从全量的三百多条用例压缩到七八十条时间成本降下来一大半而且不会漏掉关键路径。这个思路跟实际工作中的测试策略是完全一致的面试时讲出来也很加分。2. 拿到源码第一步环境搭建与工程结构速读源码到手很多人的第一反应是双击打开项目再看 README。我建议反过来先看 README 里的依赖清单和启动说明再动手环境。因为前后端分离项目的启动依赖往往是链式的后端没起来前端页面就是个白屏数据库没连上后端启动直接报错顺序错了你要花大量时间去排查本来不存在的Bug。2.1 环境清单与版本对齐我整理了一份实际用到的环境清单版本号是我实测能跑通的组合写在这里省得你一个个试组件推荐版本说明JDK1.8 或 11后端 SpringBoot 工程版本别乱升容易踩依赖冲突Maven3.6用于拉取后端依赖MySQL5.7 / 8.0建库建表脚本一般在工程根目录的 sql 文件夹里Redis5.0登录态和部分缓存会用到不装也能跑但部分功能会失效Node.js14 / 16前端构建用版本过高可能报 OpenSSL 相关错误浏览器Chrome 最新版UI 自动化调试用接口工具Postman / Apifox抓包与手工接口验证这里有个很容易忽略的点Node.js 16 以上版本在跑老一点的前端工程时经常会报error:0308010C:digital envelope routines::unsupported。解决办法是降级到 16或者设置环境变量NODE_OPTIONS--openssl-legacy-provider。我第一次遇到时以为是工程坏了折腾了两个小时才发现是版本问题。这种坑不写进教程里但你实际一定会撞上。2.2 前后端分离工程目录怎么读后端工程一般是标准的 Maven 多模块结构我读源码的顺序是pom.xml-application.yml-controller层 -service层 -mapper层。先看pom.xml是了解这个项目用了哪些依赖比如有没有集成 Redis、有没有用 MyBatis-Plus、有没有引入消息队列这些信息直接决定了后面测试要注意什么。再看application.yml重点看数据库连接、Redis 地址、文件上传路径、以及有没有配置多环境 profile因为测试环境往往和开发环境配置不同接口地址要换。Controller 层是测试人员最该花时间的地方。每一个RequestMapping就是一个接口入口把 URL、请求方法、入参类型、返回结构抄到一张表里这张表就是你后面写接口用例和自动化脚本的基础。我一般会顺手在表里加两列是否依赖登录态和是否有前置数据依赖这两列在写自动化脚本时能省掉大量返工。前端工程相对简单src/api目录下集中放着所有接口调用封装src/views是页面src/router是路由配置。把api目录看一遍你会发现有些接口后端有、前端没调用这些孤儿接口恰恰是容易被测漏的地方也是出问题概率偏高的地方。2.3 数据库与依赖服务准备建库脚本导入之后我建议做三件事一是确认表结构里有没有初始化数据比如书籍分类字典、默认用户二是手工插几条测试数据尤其是长文本、特殊字符、超长书名这类后面测边界值用得上三是把数据库的字符集确认成utf8mb4不然存 emoji 或者生僻字会直接报错。Redis 这块如果项目用它存登录 token那你测试的时候就需要注意 token 的过期时间。有些工程配置的是 30 分钟有些是 7 天。测试登录态失效场景时最快的办法是直接在 Redis 里把对应 key 删掉然后立刻发请求验证服务端的处理逻辑比等半小时要高效得多。这个技巧我在实际工作中经常用比改配置重启服务快太多。2.4 启动顺序与冒烟验证启动顺序我总结成一句话数据库 - Redis - 后端 - 前端。每一层起来之后都做一次最小验证别一口气全启动完再排查。后端起来之后先验证健康检查接口或者随便调一个不依赖数据库的接口看能不能通然后再调一个查数据库的接口确认数据源配置没问题最后调一个依赖登录态的接口确认 Redis 和拦截器都正常。前端起来之后打开首页看有没有出现接口报错F12 的 Network 面板是最好的排查入口——页面白屏、数据不显示、按钮点击无反应这三类问题九成都能在 Network 面板里找到答案。提示启动失败时优先看日志的最后 30 行而不是从头看。绝大多数启动异常的原因都在最后几行里比如端口占用、数据库连接超时、Bean 创建失败。冒烟测试跑通才算真正拿到项目。这一步没做扎实后面所有测试都是在流沙上盖房子。3. 功能测试用例设计听书类产品的核心场景拆解功能测试是这套教程的主体也是最容易被写成说明书复读机的部分。我见过不少人写的用例是点击播放按钮验证开始播放这种用例执行一百条也发现不了真问题。真正有价值的用例是在状态组合里找矛盾。3.1 核心业务链路用例怎么铺听书产品最核心的场景是播放而播放本身不是一个孤立动作它牵扯到用户状态、书籍状态、网络状态、设备状态四重维度。我把这四重维度列成组合矩阵再挑出有意义的组合去设计用例效果比凭感觉写要好得多。举个具体的例子。用户 A 在手机上听一本书听到第 12 章 30 分 15 秒然后换到 Web 端登录同一账号打开这本书应该发生什么合理的预期是续播到同一位置。那如果 A 在手机上把这本书移出了书架Web 端还在播放页此时刷新页面应该怎么表现再进一步如果这本书被后台下架了正在播放的用户应该收到什么提示这三个问题分别对应多端同步、状态冲突、内容变更三类缺陷在真实产品里都是高频问题。我给自己定的用例颗粒度标准是一条用例只验证一个预期结果但必须交代清楚前置状态。前置状态写得越具体执行时越不容易漏掉关键步骤后面别人接手回归时也看得懂。比如用户已登录且书架中已有该书且播放进度为第 5 章这样的前置比用户已登录要有用得多。3.2 边界值、异常与兼容性场景边界值这块听书类产品有几个固定的高发点我把它们整理在一张表里你照着测基本能覆盖大部分问题测试点典型边界预期表现搜索关键词空、1 字符、最长字符、纯符号、emoji空值不请求接口或有提示超长截断或提示播放进度上报0 秒、总时长、超过总时长不允许越界超过时按最大值处理书架数量0、1、上限值、上限1达到上限有明确提示评论内容空、1 字、最大长度、最大长度1前后端双重校验不能只靠前端分页参数page0、page-1、pageSize 超大服务端兜底不返回全表数据这里我要专门强调前后端双重校验这件事。前端做了长度限制不代表后端可以不做因为接口是可以被直接调用的。我在测评论接口时用 Postman 绕过前端直接提交一条 5000 字的评论如果后端没做校验就直接入库了这就是一个实实在在的缺陷。这类问题在面试里也是高频考点因为它体现的是测试思维而不是点点点。兼容性方面Web 端至少覆盖 Chrome 和另外一个主流内核浏览器移动端如果有 H5 页面重点测不同屏幕尺寸下的布局错位和按钮遮挡。我一般用浏览器的设备模拟功能快速过一遍发现可疑的再上真机。3.3 用例管理表格与缺陷记录规范用例管理我用的是最朴素的方式Excel 或者在线表格字段包括用例编号、模块、前置条件、操作步骤、预期结果、优先级、执行结果。不用追求工具多高级关键是字段要固定不然写了三百条之后你会发现格式乱套没法统计通过率。缺陷记录我有一条自己的规矩标题要能被单独读懂。好的标题是播放页在 WiFi 切 4G 后进度条归零且不恢复坏的标题是播放有问题。前者扫一眼就知道是什么问题、在什么场景下出现后者必须点进去看详情。写标题时把场景 现象两段式套进去基本就合格了。正文里必须附上环境信息、复现步骤、实际结果、预期结果、以及截图或录屏尤其是偶现问题没有日志基本等于没提。注意偶现缺陷不要急着关掉也不要只写一句无法复现。把当时的日志、时间点、网络状态记下来往往能在后续回归时定位到规律。4. 接口自动化实战从抓包到测试框架落地功能测试跑完一轮接下来该上自动化了。我的建议是自动化从接口层做起不要一上来就啃 UI。原因很直接接口自动化开发成本低、执行速度快、稳定性高投入产出比远高于 UI 自动化。UI 自动化留给最核心、最稳定的几条冒烟链路就够了。4.1 抓包与接口契约梳理写脚本之前先把接口梳理清楚。方法很简单打开浏览器 F12 的 Network 面板把页面的主要操作走一遍把请求全部录下来然后导出成 HAR 文件或者手工整理成表。整理时重点看四样东西请求 URL、请求方法、请求头里的关键字段尤其是 token 怎么带、响应体的结构。响应体结构决定了你的断言怎么写。如果服务端返回的是统一格式比如{code: 200, msg: success, data: {...}}那你的断言就应该分层先断言code再断言data里的关键字段。只断言 HTTP 状态码是远远不够的因为很多业务错误照样返回 200。4.2 pytest requests 框架的分层设计Python 技术栈我推荐 pytest requests allure这套组合上手快、生态成熟。框架我习惯分成四层配置层放环境地址、账号信息、数据库连接用配置文件或环境变量管理切换测试环境只改一处封装层对 requests 做二次封装统一处理请求头、超时、日志、token 注入用例层只写业务逻辑和断言不关心底层怎么发请求数据层测试数据单独放支持参数化分层的好处是维护成本低。比如 token 的获取方式变了你只改封装层一处几百条用例都不用动。我见过把所有逻辑写在一个文件里的脚本改一次登录逻辑要全文替换那种脚本活不过两个月。4.3 关键代码实现先看统一请求封装这部分核心是把 token、超时、日志这三件事集中处理# common/request_util.py import requests from common.logger import logger class RequestUtil: def __init__(self, base_url): self.base_url base_url self.session requests.Session() def send(self, method, path, tokenNone, **kwargs): url self.base_url path headers kwargs.pop(headers, {}) if token: headers[Authorization] fBearer {token} kwargs.setdefault(timeout, 10) logger.info(f请求 {method} {url}, 参数: {kwargs}) resp self.session.request(method, url, headersheaders, **kwargs) logger.info(f响应状态: {resp.status_code}, 内容: {resp.text[:500]}) return resp再看用 pytest 写的用例配合 fixture 管理登录态和数据清理# testcases/test_book.py import pytest from common.request_util import RequestUtil pytest.fixture(scopesession) def client(): return RequestUtil(http://localhost:8080) pytest.fixture(scopesession) def token(client): resp client.send(POST, /api/login, json{username: tester, password: 123456}) return resp.json()[data][token] pytest.mark.parametrize(keyword, [, 测试, a * 200, #$%]) def test_search_book(client, token, keyword): resp client.send(GET, /api/book/search, tokentoken, params{keyword: keyword}) body resp.json() assert body[code] in (200, 400) if body[code] 200: assert isinstance(body[data][list], list)这段用例的设计要点在parametrize那一行把搜索关键词的边界值直接做成参数一条函数覆盖四种场景比复制粘贴四遍要清爽得多而且后面要加新的关键词只改列表就行。4.4 断言与数据驱动断言我坚持一个原则断言业务字段不断言无意义的文本。比如返回体里有个createTime字段你去断言它等于某个固定字符串那这条用例明天就会红因为时间一直在变。正确的做法是断言它符合时间格式或者断言它大于某个基准时间。数据驱动方面我一般把测试数据放在 yaml 或者 csv 里用 pytest 的pytest.mark.parametrize读取。这样做的好处是数据和代码分离业务方改数据不用碰代码也让用例看起来干净。当用例数量上到一两百条之后你还会需要按模块打mark标签方便在 CI 里分批次跑比如pytest -m smoke只跑冒烟集。5. UI自动化与持续集成跑得稳比跑得快更重要接口自动化解决了逻辑对不对UI 自动化解决的是用户能不能用。这两者的定位完全不同千万不要用 UI 自动化去覆盖大量业务分支那样你每天的维护时间会超过写用例的时间。5.1 元素定位与 PO 模型UI 自动化的第一道坎是元素定位。优先级我建议是id name css selector xpath。xpath 放在最后是因为它太依赖页面层级前端改一下 DOM 结构你的定位就全废了。如果实在只能用 xpath尽量避免用绝对路径改成用文本或属性做相对定位。页面对象模型PO是必须用的。核心思想是页面元素和操作封装成类用例只调用方法。这样页面改了你只改页面类用例逻辑变了你只改用例层。我刚开始写 UI 自动化时不用 PO结果登录页改了一次按钮的 class二十多条用例全挂那一刻我才明白为什么所有教程都在强调 PO。5.2 等待策略与稳定性优化UI 自动化最头疼的是元素找不到而绝大多数找不到都是时机问题不是定位问题。三种等待方式里我推荐显式等待为主隐式等待兜底强制等待尽可能不用。强制等待就是sleep(3)那种它的坏处是快的时候浪费三秒慢的时候三秒又不够。显式等待是等某个条件成立比如元素可见、元素可点击这样无论页面快慢都能自适应。隐式等待是全局设置一个最大等待时间元素一开始找不到就轮询作为兜底比较省事但它对元素存在但不可点击这种情况无效所以还是要配合显式等待。我在实际项目里还做了两件事来提升稳定性一是每次失败自动截图并保存页面源码方便事后定位二是在关键操作前后加日志出问题时能一眼看出卡在哪一步。这两件事听起来麻烦但比起每天花两小时排查偶发失败前期多写的这点代码太值了。5.3 集成到持续集成流水线本地跑得再好不接进流水线就没法发挥价值。我的做法是把接口自动化和 UI 自动化分开接口自动化每次代码提交都跑因为它快几分钟就能出结果UI 自动化每天定时跑一次或者每晚跑一次因为它慢且对环境要求高。流水线里跑失败一定要能通知到人邮件、企业微信或者别的通知方式都行关键是别让失败静静地躺着。我见过很多团队接了 CI 但没人看报告结果自动化用例红了两个月都没人管那还不如不接。失败通知里要带上有用的信息哪条用例失败、失败原因、报告链接、截图路径。信息越完整排查越快。6. 性能测试与常见问题排查实录性能这块很多人直接跳过觉得练手项目没必要。我的看法是做一个最小规模的性能基线成本很低但能让你在面试里多聊十分钟而且能真正理解并发到底意味着什么。6.1 性能场景设计与指标口径练手项目的性能测试不用追求大并发20 到 50 个虚拟用户跑起来就足够暴露问题了。场景我一般设计三个登录接口、书籍列表查询、播放进度上报。前两个是典型的读操作最后一个是写操作三类覆盖下来基本能看出系统的大致承载能力。指标口径要提前定好不然跑出来的报告没法解读。我盯的主要是四个数平均响应时间、90% 响应时间、错误率、吞吐量。其中90% 响应时间比平均值更有参考价值因为平均值会被少数极快的请求拉低掩盖掉那些慢请求。比如平均 200 毫秒听起来很好但 90% 线是 2 秒说明有相当一部分用户在用的时候是卡顿的。用 JMeter 的话测试计划里记得加聚合报告和响应时间分布图另外把断言加上URL 地址、响应码都要校验。不加断言的压测结果里那些 200 毫秒返回的错误页面会被当成成功请求统计数据全失真。6.2 常见问题速查表下面这张表是我在这套项目里实际遇到并且记录下来的问题你可以直接拿去当排查手册用现象可能原因排查方向前端页面白屏控制台无报错前端未启动或静态资源路径错检查前端进程、F12 看资源加载状态码接口返回 401token 缺失或过期检查请求头、Redis 中 key 是否还在接口返回 500日志有 SQL 异常字段长度超限或类型不匹配看日志中的 SQL 语句和参数值列表数据不更新缓存未失效检查 Redis 中对应 key确认是否有缓存过期策略上传接口报错文件路径不存在或无写权限检查配置中的上传目录是否存在压测中错误率突然升高连接池耗尽或数据库瓶颈看后端日志、数据库慢查询、连接数监控自动化用例偶发失败元素未加载完成改用显式等待截图看实际页面状态这张表的价值在于它缩短了从发现问题到定位原因的时间。实际工作中测试人员最值钱的能力不是发现 Bug而是给出可信的定位方向因为开发拿到你的方向能直接去看对应代码而不用从头查一遍。6.3 独家避坑心得最后分享几条我在这套项目上花过时间换来的经验都是常规文档里不会写的。第一条改配置之前先备份。测试过程中你肯定会改一些环境配置方便测试比如把 token 过期时间调短、把文件上传大小限制调小。改完之后一定要记下来或者备份原文件否则测完忘了改回去下一轮测试就会出现莫名其妙的问题你还以为是新 Bug。第二条用版本管理工具管你的测试脚本。用例、脚本、数据文件都放进 Git每次改动都有记录。我吃过亏改了脚本之后发现新脚本有问题想退回上一版结果发现没有版本记录只能重写。现在我的习惯是每完成一个模块的脚本就打一次 tag心里特别踏实。第三条别追求一次写完美的框架。我见过太多人卡在框架搭不完所以用例没法写这一步。正确的顺序是先写两三条能跑通的用例跑起来之后再慢慢抽象封装。框架是长出来的不是设计出来的。你写完第一条跑通的登录用例自然就知道哪些地方该抽公共方法了。第四个想说的是把测试过程中的思考记录下来。比如你为什么选择测这个场景而不是那个为什么某个边界值你觉得重要。这些思考在写简历、准备面试的时候就是现成的素材。很多人测完一个项目问他学到了什么只能说出我测了登录和搜索这就浪费了项目本身的价值。这套东西我后来又做了一次扩展把接口自动化接进了流水线又补了一份简单的性能基线报告。如果你也想往下走一步我建议的顺序是先把功能测试跑透形成完整的用例集然后把接口自动化做起来覆盖核心链路最后再去补 UI 自动化和性能。每完成一层都停下来复盘一次看看哪些设计是合理的、哪些是当时图省事留下的坑。这种复盘带来的成长比你多测两个模块要大得多。
阅读完成 · 觉得有帮助?