Vibe Coding 项目的功能测试与自动化工作流:从“打地鼠“到“安全网“
工试云启 考证服务中心整理

文档教程Vibe Coding示例工程【免费下载链接】vibe-vibeThe First Systematic Vibe Coding Open-Source Tutorial | From Zero to Full-Stack, Empowering Everyone to Build Products with AI | Live at: www.vibevibe.cn 首个系统化 Vibe Coding 开源教程 | 零基础到全栈实战让人人都能用 AI 开发产品 | 在线地址www.vibevibe.cn项目地址https://gitcode.com/datawhalechina/vibe-vibe点击查看免费下载本文以 vibe-vibe 开源教程《第九章功能测试与自动化》为核心系统讲解回归测试与测试金字塔的核心理念、API 测试与 E2E 测试的实战方法含 Flaky 测试排查与日志阅读以及 Git Hooks GitHub Actions 的自动化防线与 TDD 入门。读完你将掌握一套完整的机器替你检查每次改动的测试体系并结合仓库内真实的 Vitest 测试代码与 API 实现获得可直接复用的实战范式。一、为什么需要测试从打地鼠困境到安全网1.1 一个典型的修 A 坏 B事故小明的个人豆瓣越来越像样了用户系统跑通、CRUD 没问题、安全防护到位。他决定加一个电影评分功能——用户给每部电影打 1 到 5 颗星页面显示均分。功能不复杂让 Claude Code 很快写了出来自己点几遍没发现问题很满意。但第二天朋友发来消息搜索坏了。输入关键词点搜索页面一片空白。小明一头雾水昨天只改了评分功能压根没碰搜索。打开代码才发现评分功能新增的数据查询函数修改了一个公共的数据库查询模块——而搜索功能也依赖这个模块。改评分的时候搜索被误伤了。他花了半小时修好搜索第二天又有人说电影详情页打不开——原来修搜索时不小心改了路由配置修好详情页当晚一测评分功能本身又坏了。三天、三个 bug每个都是修前一个 bug 时引入的。这就是回归 bug以前能用的功能改了别的代码之后不能用了。1.2 安全网是什么这个困境的本质是功能之间并不是完全独立的它们共享数据库连接、公共工具函数、路由配置。改动一个公共模块影响链在人的脑子里很难追踪——而代码库越大这种依赖越复杂。安全网就是一组自动化检查。每个检查对应一个功能点搜索科幻能返回结果吗给电影打 4 星能成功吗未登录用户访问评分接口会被拒绝吗这些检查写成代码存放在项目里每次改完代码运行一遍——全部通过说明没有误伤某个失败说明刚才的改动破坏了什么当场就能定位。这就是回归测试以前能用的功能改了别的代码之后还能用吗1.3 为什么人肉回归靠不住人肉回归也有价值模拟真实用户路径能发现自动化覆盖不到的体验问题按钮点击区域太小、加载后页面闪烁、提示文案奇怪。但真正扛不住的不是点一遍慢而是重复——一天改三次代码就要点三遍第一遍认真、第二遍走马观花、第三遍跳过觉得不会出问题的部分而 bug 往往就藏在跳过的部分。更重要的是人会做聪明判断——我只改了评分搜索肯定没事——然后跳过搜索的测试。这个判断大部分时候对但错的那一次就是 bug。机器不会做这种判断你让它检查 15 个点它就老老实实检查 15 个点每次都一样。还有一个容易被忽略的好处自动化测试是活的文档。三个月后你忘了评分接口的返回格式测试文件里写得清清楚楚——expect(res.status).toBe(200)、expect(res.body.averageRating).toBeCloseTo(4.0)。注释可能过时但测试过时了会直接报红。二、测试金字塔不是所有测试都一样小明的困惑是该打开浏览器模拟点击还是直接调用函数看返回值答案是两者都要但成本和价值不同。2.1 三层测试的分工单元测试测试最小的代码单元一个函数、一个工具方法。比如calculateAverageRating([4, 5, 3])期望返回4——直接调用、看返回值不需要服务器、数据库、浏览器跑一次几毫秒。价值在于精确定位测试失败立刻知道问题出在这个函数且不依赖外部环境极其稳定。适合单元测试的还有搜索关键词清洗函数、日期格式化函数、评分星级显示逻辑。集成测试API 测试测试多个模块协作的结果——路由解析、请求校验、数据库操作、响应格式化串在一起能否正常工作。比如POST /api/movies/:id/rate需要启动服务器、连接数据库、发 HTTP 请求、检查状态码和数据、再查库确认记录确实写入。它能发现单元测试发现不了的接缝问题比如路由把用户提交的4字符串传给函数时忘了转数字——单元测试只测函数本身发现不了这个配合问题。E2E 测试模拟真实用户的完整操作流程——打开浏览器、登录、找电影、点星星、提交、看页面均分更新。最接近真实体验但要启动真实浏览器跑一次几秒到十几秒而且很脆弱——按钮换位置、加载慢、弹窗遮挡都可能导致失败。维护成本最高。2.2 金字塔的经济学三层构成经典的测试金字塔底层单元测试最多快、稳、便宜多写无妨、中层集成测试适量覆盖核心接口、顶层 E2E 测试最少只测最关键的用户流程。为什么不全部用 E2E 一步到位因为成本差距巨大50 个测试场景全用单元测试跑完不到 1 秒全用 E2E 可能要 5 分钟UI 改一个按钮位置所有涉及该按钮的 E2E 测试都要更新而单元/集成测试完全不受影响。金字塔不是教条而是经济学——用最低的成本获得最高的信心。一个简单判断标准如果这个逻辑不依赖 UI 就能验证就不要用 E2E 测试。评分计算对不对单元测试。接口返回数据对不对集成测试。用户点按钮能不能完成操作这才需要 E2E。// 单元测试测纯函数不需要任何外部依赖 test(calculateAverageRating 计算均分, () { expect(calculateAverageRating([4, 5, 3])).toBe(4) expect(calculateAverageRating([5])).toBe(5) expect(calculateAverageRating([])).toBe(0) // 边界空数组 }) // 集成测试测 API 接口需要服务器和数据库 test(POST /api/movies/:id/rate 提交评分, async () { const response await request(app) .post(/api/movies/42/rate) .set(Authorization, Bearer ${token}) .send({ rating: 4 }) expect(response.status).toBe(200) expect(response.body.averageRating).toBe(4) }) // E2E 测试模拟用户操作需要浏览器 test(用户给电影打分, async ({ page }) { await page.goto(/movies/42) await page.click([data-testidstar-4]) await page.click([data-testidsubmit-rating]) await expect(page.locator(.average-rating)).toHaveText(4.0) })三、什么时候该引入自动化测试自动化测试是一笔投资——写测试和维护测试都要时间投入要能换来回报。该上的信号项目有 5 个以上页面、核心业务流程搜索→详情→评分超过 3 步、已经遇到过修 A 坏 B。这些信号说明复杂度已经超过人肉回归能可靠覆盖的范围。更隐蔽的信号你开始害怕改代码了。知道某块代码写得不好但不敢重构——这种不敢动的心态是代码腐化的开始。有了安全网你可以放心重构改完跑测试绿了就没问题。不该上的场景需求频繁变化的原型阶段今天的评分明天可能换成点赞、一次性项目、纯静态展示页。还有一种常见误区为了覆盖率数字而写测试。覆盖率只告诉你哪些代码被执行过不告诉你有没有验证正确的行为——你可以写一个调用所有函数但不做任何断言的测试覆盖率 100%但什么也没测到。关注测试的价值而不是覆盖率的数字。先测什么问自己如果这个功能坏了后果有多严重。评分计算错了用户看到错误均分——严重优先测列表排序方式变了——不太严重可以后测404 页面样式歪了——无所谓不测。用 20% 的测试覆盖 80% 的风险。四、AI 辅助测试的正确姿势让 Claude Code 写测试完全可行给评分接口写测试覆盖正常、异常、边界场景它会分析接口代码生成包含数据准备与清理的完整测试文件。但测试和业务代码有个关键区别测试的价值取决于测的是不是关键场景这需要你对业务的理解。AI 能读到已实现的逻辑并测试它但如果某个业务规则还没写进代码、只存在于你的脑子里比如同一用户重复评分时应该更新旧评分而不是新增AI 不会凭空想到要测它。正确分工是你把控测试策略AI 执行和补充。审查 AI 生成的测试时关注两件事覆盖度四类场景正常、校验、权限、边界都覆盖了吗尤其是边界场景——对应业务规则还没实现时AI 不会想到。真实性测试数据是否贴近真实使用太干净的数据全是英文、全是正整数可能漏掉真实环境问题中文输入、特殊字符、浮点数精度。推荐 prompt阅读我的项目代码分析哪些模块最需要测试。列出建议的测试优先级哪些先写单元测试哪些需要集成测试哪些需要 E2E 测试。变异测试是验证测试质量的实用技巧故意在业务代码里引入一个 bug比如把评分范围校验从1-5改成1-10然后跑测试。如果测试没有报红说明它没有真正验证这个约束需要补断言。五、API 测试实战为什么先测接口5.1 三个理由定位问题评分页面报错如果没有 API 测试你得打开浏览器、登录、找电影、点评分、看结果走完整流程才能判断是前端还是后端问题。有了 API 测试直接发请求到POST /api/movies/42/rate——返回 200 且数据正确问题在前端返回 500问题在后端。API 测试帮你把问题定位到前端还是后端层面。API 比 UI 稳定UI 经常改按钮换位置、改样式、加动画每次 UI 变动都可能让依赖 UI 元素的测试失败但 API 契约相对稳定——发这个请求返回这个数据不会因为按钮换颜色就变。API 测试写一次能用很久。成本低写起来快、跑起来快、维护成本低。5.2 一个接口至少测四类场景以评分接口POST /api/movies/:id/rate为例场景输入期望正常路径已登录用户提交 1-5 的整数评分200 正确均分参数校验评分 0 或 6超范围、字符串abc400 错误提示权限控制未登录用户提交评分401边界情况同一用户重复评分应更新旧记录、不存在的电影 ID200更新/ 404import { describe, test, expect, beforeEach } from vitest describe(POST /api/movies/:id/rate, () { // 正常路径 test(已登录用户提交合法评分, async () { const res await request(app) .post(/api/movies/42/rate) .set(Authorization, Bearer ${userToken}) .send({ rating: 4 }) expect(res.status).toBe(200) expect(res.body.averageRating).toBeCloseTo(4.0) }) // 参数校验 test(评分超出范围返回 400, async () { const res await request(app) .post(/api/movies/42/rate) .set(Authorization, Bearer ${userToken}) .send({ rating: 6 }) expect(res.status).toBe(400) }) // 权限控制 test(未登录用户返回 401, async () { const res await request(app) .post(/api/movies/42/rate) .send({ rating: 4 }) expect(res.status).toBe(401) }) // 边界情况 test(重复评分更新旧记录, async () { await request(app) .post(/api/movies/42/rate) .set(Authorization, Bearer ${userToken}) .send({ rating: 3 }) const res await request(app) .post(/api/movies/42/rate) .set(Authorization, Bearer ${userToken}) .send({ rating: 5 }) expect(res.status).toBe(200) // 应该是更新不是新增 expect(res.body.averageRating).toBe(5) }) test(电影不存在返回 404, async () { const res await request(app) .post(/api/movies/99999/rate) .set(Authorization, Bearer ${userToken}) .send({ rating: 4 }) expect(res.status).toBe(404) }) })这四类不是教条而是思维方式——每次写测试时问自己正常情况能用吗错误输入会被拦住吗没权限的人能访问吗有没有我知道但 AI 不知道的特殊情况5.3 仓库中的真实范例demo-01-todo 的 API 测试vibe-vibe 仓库的 demos/demo-01-todo 项目提供了一个可以直接对照学习的真实案例。其tests/todos.test.ts 使用Vitest对 Todo API 的GET /api/todos和POST /api/todos进行了完整测试正好对应上面四类场景中的正常路径与参数校验import { describe, it, expect, vi, beforeEach } from vitest import { NextRequest } from next/server // 模拟数据库模块 vi.mock(../src/db, () ({ db: { select: vi.fn().mockReturnThis(), from: vi.fn().mockReturnThis(), where: vi.fn().mockReturnThis(), orderBy: vi.fn().mockResolvedValue([]), insert: vi.fn().mockReturnThis(), values: vi.fn().mockReturnThis(), returning: vi.fn().mockResolvedValue([{ id: 1, title: 测试, completed: false }]), // ... }, })) // 导入路由处理函数 import { GET, POST } from ../src/app/api/todos/route describe(Todo API, () { describe(POST /api/todos, () { it(应该创建新 todo 并返回 201, async () { const request new Request(http://localhost/api/todos, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ title: 学习数据库 }), }) const response await POST(request) expect(response.status).toBe(201) const data await response.json() expect(data).toHaveProperty(id) expect(data).toHaveProperty(title) }) it(标题为空时应该返回 400, async () { const response await POST(new Request(http://localhost/api/todos, { method: POST, headers: { Content-Type: application/json }, body: JSON.stringify({ title: }), })) expect(response.status).toBe(400) }) }) })这个测试有几个值得学习的要点vi.mock模拟数据库测试完全隔离了真实数据库本仓库 demo-01-todo 使用 Drizzle ORM Neon用vi.fn()链式模拟查询构建器让测试无需真实数据库即可运行——这正是单元/API 测试快速、稳定、不依赖外部环境的体现。直接调用路由处理函数因为 Next.js 的 Route Handler 就是普通函数测试可以直接import { GET, POST } from ../src/app/api/todos/route并传入NextRequest无需起真实 HTTP 服务器。配置极简对应的 vitest.config.ts 只配置了路径别名指向./src没有任何复杂插件package.json 中test: vitest run一行脚本即可运行Vitest 作为 devDependency 声明。测试所验证的业务逻辑在 src/app/api/todos/route.ts 中真实存在POST处理函数用 src/lib/validation.ts 中定义的createTodoSchematitle非空、最长 200 字做safeParse校验失败返回 400成功则写入数据库并返回 201——测试与实现一一对应这正说明测试是活的文档。5.4 让 AI 生成 API 测试跟 AI 说阅读app/api文件夹下的所有路由为每个 API 生成 Vitest 测试。每个接口覆盖正常请求200、参数错误400、未授权401。AI 会遍历接口文件分析每个路由的参数、返回值和中间件生成对应测试代码。你要审查的不是语法而是覆盖度有没有你计划实现但还没写的业务规则有没有代码里没有显式处理的边界情况并发提交、极端数据量这些需要你作为业务理解者来补充。六、E2E 测试从接口到完整用户体验API 测试全过了评分功能就稳了吗不一定——朋友反馈点了评分按钮没反应。排查发现 API 本身没问题问题在前端重构组件时把提交逻辑移到了另一个文件但忘了在新组件里引入。TypeScript 编译不报错函数签名正确只是没被调用页面正常渲染按钮在点了没反应。API 测试测不到这个问题因为它绕过了前端直接发请求。这就是 E2E 测试的价值测用户能不能完成他想做的事不关心API 能不能用或函数返回值对不对。6.1 Playwright 与录制模式Playwright是当前最主流的 E2E 工具微软开源能自动打开 Chrome、Firefox、Safari模拟点击、输入、滚动还能截图和录屏。特别实用的功能是录制模式你在浏览器里操作它自动生成测试代码。运行npx playwright codegen http://localhost:3000即可启动生成的代码可能需要微调选择器不够稳定但作为起点比从零写快得多。小明的第一个 E2E 测试用户登录后给电影打分。import { test, expect } from playwright/test test(用户给电影打分, async ({ page }) { // 登录 await page.goto(/login) await page.fill([nameemail], mingexample.com) await page.fill([namepassword], password123) await page.click(button[typesubmit]) // 等待登录完成跳转到首页 await page.waitForURL(/) // 进入电影详情页 await page.click(text星际穿越) await page.waitForURL(/movies/**) // 点击 4 星 await page.click([data-testidstar-4]) await page.click([data-testidsubmit-rating]) // 验证均分更新 await expect(page.locator(.average-rating)).toContainText(4) })注意waitForURL——这些是显式等待告诉 Playwright等到页面跳转完成再继续。如果不等待脚本可能在页面还没加载完就去找元素找不到就报错。这是 E2E 测试最常见的坑之一。6.2 E2E 与 API 测试怎么选API 测试覆盖数据对不对状态码、数据格式、业务逻辑。大部分功能核心逻辑都可以用它验证。E2E 测试覆盖用户能不能用页面能不能打开、按钮能不能点、流程能不能走通。只用在最关键的用户流程注册登录、核心业务操作评分、搜索、支付流程。一个常见误区是E2E 更真实所以更好。真实不等于更好——E2E 维护成本最高评分页面改一次布局所有涉及评分的 E2E 测试都要更新选择器而 API 测试完全不受影响。判断标准如果一个 bug 只能通过 E2E 发现按钮没绑事件、页面跳转错误就用 E2E如果通过 API 测试就能发现接口返回错误数据、权限没校验就用 API。能用更轻量的方式验证就不要用更重的方式。七、Flaky 测试测试界的玄学7.1 三大成因与对策同一个 E2E 测试跑三次通过、通过、失败——这就是Flaky 测试有时通过有时失败。它是最让人头疼的 E2E 问题成因通常有三种成因典型场景对策异步时序点了提交评分测试立刻断言页面均分但请求还没返回显式等待expect(locator).toContainText()默认等 5 秒特殊慢操作手动加长超时数据竞争测试 A 给电影 42 打 5 分测试 B 断言电影 42 均分为 0A 先跑则 B 挂数据隔离每个测试独立准备数据、结束后清理不共享状态环境差异本地网络快测试顺畅CI 虚拟机性能差、延迟高同样等待时间不够给断言设置合理超时不要假设操作瞬间完成Flaky 测试不能忽略。偶尔失败意味着你永远不确定测试结果是否可信——安全网上有个洞你就不敢信任它。要么修好要么暂时标记skip并记录原因不要让它一直在那里偶尔红。7.2 判断 Flaky 还是真 bug连续跑 5 次5 次全失败大概率是真 bug几次过几次不过就是 Flaky。Playwright 支持--repeat-each5参数让每个测试重复跑 5 次。修 Flaky 的核心原则不要用sleep来等一等。await page.waitForTimeout(2000)看起来解决了问题但只是把问题藏起来——本地 2 秒够用CI 上可能不够今天够用数据量大了以后可能不够。正确做法是条件等待await page.waitForSelector(.average-rating)等到元素出现、await expect(locator).toContainText(4)等到文本变成预期值。条件等待在条件满足的瞬间继续既不会等太久浪费也不会等太短导致 Flaky。跟 AI 说这个 E2E 测试有时候通过有时候失败帮我分析原因并修复。错误信息是[粘贴错误日志]八、测试失败三步定位错误信息 → 截图 → 网络请求第一步看错误类型。Timeout waiting for selector [data-testidsubmit-rating]——等待超时元素没在规定时间内找到。可能原因页面没加载完、data-testid改了、前面的操作登录失败导致页面停在错误位置。Expected 4.0 but received 3.5——断言失败元素找到了但值不对。可能原因计算逻辑 bug、测试数据没正确准备数据库已有其他评分记录影响均分。Error: net::ERR_CONNECTION_REFUSED——网络错误后端服务没启动或端口不对。第二步看截图。Playwright 失败时自动保存页面截图默认在test-results/目录。截图比日志直观得多——停在登录页登录失败显示错误提示API 返回错误页面空白前端渲染崩了一张截图往往就定位了问题。第三步看网络请求。Playwright 可配置 trace追踪记录测试过程中每个网络请求和响应。找到评分提交请求——返回 200 还是 500如果 API 返回 500 问题在后端API 返回正确数据但页面没更新问题在前端渲染逻辑。// playwright.config.ts export default defineConfig({ use: { // 只在测试失败时保存 trace trace: on-first-retry, // 失败时自动截图 screenshot: only-on-failure, }, })查看 tracenpx playwright show-trace test-results/xxx/trace.zip这会打开可视化界面可逐步回放测试过程看到每一步的页面状态、网络请求和控制台日志。三步走完还找不到就把错误日志和截图一起丢给 AI 分析。九、自动化工作流让机器记住每次都要检查9.1 核心规律bug 发现得越晚修复成本越高测试全绿后小明继续开发新功能每次都想着等会儿跑测试但总被下一个功能吸引过去忘了。三天后推代码到 GitHub朋友拉下来一跑——两个测试挂了是他第一天改搜索排序时引入的 bug。如果当天就跑测试当场就能发现拖了三天要从三天的改动里找出是哪次提交引入的问题排查成本翻了好几倍。自动化测试的核心价值不是帮你找 bug手动测也能找到而是帮你尽早找到 bug在修复成本最低的时候。老师傅说人会忘机器不会。让机器帮你记住。9.2 Git HooksHusky提交前自动检查——第一道防线Git 内置Hooks机制配置脚本让 Git 在特定操作时自动执行。pre-commithook 在每次git commit前自动运行脚本失败返回非零退出码则 Git 阻止提交。就像门禁系统刷卡进门系统自动检查权限不用自己记住要检查。配置 Git Hooks 最常用的工具是Husky它帮你管理 hooks 脚本不用手动去.git/hooks/目录折腾跟 AI 说帮我配置 Git pre-commit hook在每次 commit 前自动运行pnpm test。测试失败就阻止提交。用 Husky。# 安装 Husky pnpm add -D husky # 初始化 npx husky init # .husky/pre-commit 文件内容 pnpm test以后每次git commitHusky 自动运行pnpm test——测试失败提交被阻止测试通过正常提交。这个机制的心理效果比技术效果更重要把应该做但容易忘的事变成自动发生的事是自动化的核心价值。局限pre-commit hook 只在本地运行有人可以用git commit --no-verify跳过。--no-verify有合理使用场景WIP 提交、纯文档改动但关键是有意识地跳过而不是养成习惯——如果你经常跳过说明 hook 配置得太重应该优化 hook 本身而不是绕过它。9.3 GitHub Actions推送后自动验证——第二道防线代码推到 GitHub 后GitHub Actions会在一个全新的、干净的虚拟机上自动运行测试。它的核心价值不只是再跑一遍而是干净环境从零开始——安装依赖、构建、跑测试能发现在我电脑上能跑的问题忘了把环境变量加进.env.example、本地缓存了旧构建产物导致测试意外通过、本地装了全局工具但package.json没声明依赖。跟 AI 说帮我写一个 GitHub Actions workflow在每次 push 和 PR 时自动运行测试。# .github/workflows/test.yml name: Test on: push: branches: [main] pull_request: branches: [main] jobs: test: runs-on: ubuntu-latest steps: - uses: actions/checkoutv4 - name: Setup Node.js uses: actions/setup-nodev4 with: node-version: 20 - name: Setup pnpm uses: pnpm/action-setupv4 - name: Install dependencies run: pnpm install - name: Run tests run: pnpm test每次 push 或创建 PRGitHub 自动启动虚拟机按流程跑一遍结果显示在 PR 页面上——绿勾通过、红叉失败。两道防线配合Git Hooks 本地快速拦截几秒钟GitHub Actions 远端全面验证几分钟。CI 失败但本地通过怎么办不要急着在本地反复跑先看 CI 日志。常见原因是环境问题而非代码问题环境变量没配在 GitHub 仓库 Settings → Secrets 配置、数据库连接失败、时区差异CI 默认 UTC本地可能是 UTC8。解决方法是让 CI 环境与本地保持一致。十、TDD先想清楚再动手10.1 测试文件就是接口规格说明小明要加电影短评功能用户写文字评论最多 500 字老师傅建议这次试试先写测试。先想清楚接口行为POST /api/movies/42/reviews接收{ content: 很好看特效震撼 }返回 201 和评论对象未登录返回 401内容为空返回 400超过 500 字返回 400。把这些写成测试代码——当然全部失败因为接口还不存在。然后告诉 Claude Code这是我写好的测试文件帮我实现对应的 API让所有测试通过。写完会发现因为先想清楚了接口设计接收什么参数、返回什么状态码、拒绝什么输入代码写起来特别顺畅。测试文件本身就是一份接口规格说明——它精确描述行为比口头描述更准确因为它是可执行的行为不一致立刻报红。10.2 Red → Green → RefactorTDD测试驱动开发的核心思路先写测试定义什么是正确的→ 再写代码让测试通过→ 最后重构优化代码保持测试通过。三步有专门名字Red写测试测试失败代码还不存在Green写最少的代码让测试通过——防止过度设计不需要一开始就考虑以后要支持半星评分怎么办Refactor优化代码结构保持测试通过需求变了先改测试定义新的正确再改代码// 第一步先写测试RED——测试失败因为接口还不存在 describe(POST /api/movies/:id/reviews, () { test(已登录用户提交短评, async () { const res await request(app) .post(/api/movies/42/reviews) .set(Authorization, Bearer ${userToken}) .send({ content: 很好看特效震撼 }) expect(res.status).toBe(201) expect(res.body.content).toBe(很好看特效震撼) expect(res.body.movieId).toBe(42) }) test(内容为空返回 400, async () { const res await request(app) .post(/api/movies/42/reviews) .set(Authorization, Bearer ${userToken}) .send({ content: }) expect(res.status).toBe(400) }) test(内容超过 500 字返回 400, async () { const res await request(app) .post(/api/movies/42/reviews) .set(Authorization, Bearer ${userToken}) .send({ content: a.repeat(501) }) expect(res.status).toBe(400) }) test(未登录返回 401, async () { const res await request(app) .post(/api/movies/42/reviews) .send({ content: 很好看 }) expect(res.status).toBe(401) }) }) // 第二步写代码让测试通过GREEN // 告诉 Claude Code这是我的测试文件帮我实现对应的 API。 // 第三步重构REFACTOR // 测试全绿后优化代码结构每次改完跑测试确认没破坏功能。10.3 TDD 的适用与不适用TDD 不是教条。适合核心业务逻辑评分计算、权限判断——算错了后果严重、需要稳定接口的模块API 端点——设计确定后不易改、复杂的条件分支多种输入对应多种输出——先列全情况再写代码不容易漏。不适合UI 样式调整按钮颜色、间距写测试没意义、探索性原型需求未定今天的测试明天全废、一次性脚本跑一次就扔的数据迁移脚本。TDD 与 AI 编程配合得特别好传统 TDD 的痛点写测试费时间现在可以用自然语言描述接口行为让 AI 生成测试代码。你的工作从写测试代码变成描述接口应该怎么工作——这其实就是设计。然后把测试文件交给 AI 实现代码。测试文件成了你和 AI 之间的契约你定义行为AI 实现行为测试验证行为。这个工作流比先写代码再补测试高效得多因为验收标准提前定义AI 不需要猜你想要什么。仓库中 demos/demo-03-social-schema/src/validated-operations.ts 提供了一个与500 字短评完全呼应的验证范例它用 drizzle-zod 从数据库 Schema 自动生成 Zod 验证规则其中评论内容正是用.max(500)限制长度并用safeParse不抛异常的验证返回{ success, data?, error? }结构化结果分别验证了合法数据与超长数据哈.repeat(501)。这展示了用测试/验证把业务约束固化成可执行规格的思路——一份定义同时用于数据库建表、类型推导和运行时验证测试或验证代码因此成为最可靠的行为契约。十一、从打地鼠到安全网测试是逐步积累的一个月前小明改代码心惊胆战朋友的 bug 报告是唯一的反馈渠道而且总是迟到。现在不同了核心接口有 API 测试关键流程有 E2E 测试每次git commitHusky 自动跑测试——全绿放心提交代码推到 GitHubActions 再跑一遍——全绿放心合并某个测试红了当场就知道是哪次改动引入的问题。测试不是负担而是让你敢改代码的底气。没有测试时不敢重构有了测试可以大胆重构——改完跑一遍绿了就没问题。第一次看到测试拦住一个 bug幸好有测试不然这个 bug 就上线了你就会理解测试的价值是实实在在的。这不是一夜之间建成的而是逐步积累先给最容易出问题的接口写测试再给核心流程加 E2E然后配上 Git Hooks 和 CI每次遇到一个 bug修完之后顺手加一个测试确保同样的 bug 不会再出现——这叫bug 驱动测试不是提前预测所有 bug而是每次被咬一口就补一个防护。不需要追求完美的测试覆盖率。80% 的风险可以用 20% 的测试覆盖。先把最关键的部分保护起来剩下的慢慢补。重要的是开始——哪怕只有一个测试也比零个强。十二、本章路线图与延伸阅读本章的三个小节可分别深入阅读9.1 为什么需要测试回归测试、测试金字塔、何时自动化、AI 辅助测试的正确姿势9.2 API 测试与 E2E 测试API 测试实战、Playwright E2E 测试、Flaky 测试排查、日志阅读9.3 自动化工作流Git Hooks、GitHub Actions CI、TDD 入门、测试成熟度在继续动手之前也可以回到第八章安全与用户认证复习认证实现——因为 API 测试中的权限控制401场景正是依赖第八章加上的认证机制来验证的。掌握了测试与自动化后下一步就是第十章Localhost 与公网访问把应用从本地搬到互联网上。核心要点回顾回归测试的核心问题以前能用的功能改了别的代码之后还能用吗测试金字塔单元测试多而快测函数、集成测试适量测接口、E2E 测试少而精测流程一个接口至少测四类场景正常、校验、权限、边界Flaky 测试三大成因与对策异步时序显式等待、数据竞争数据隔离、环境差异合理超时测试失败三步定位错误信息 → 截图 → 网络请求Git HooksHusky是第一道防线GitHub Actions 是第二道防线TDD 适合核心业务逻辑和稳定接口先写测试定义行为再写代码实现——不是教条是工具测试逐步积累先保护最关键的部分每次修 bug 顺手加测试安全网越织越密赞分享文档教程Vibe Coding示例工程【免费下载链接】vibe-vibeThe First Systematic Vibe Coding Open-Source Tutorial | From Zero to Full-Stack, Empowering Everyone to Build Products with AI | Live at: www.vibevibe.cn 首个系统化 Vibe Coding 开源教程 | 零基础到全栈实战让人人都能用 AI 开发产品 | 在线地址www.vibevibe.cn项目地址https://gitcode.com/datawhalechina/vibe-vibe点击查看免费下载相关推荐vibe-vibe 功能测试与自动化实战从回归测试、API/E2E 测试到 Git Hooks 与 CI 工作流vibe vibe 功能测试与自动化实战从回归测试、API/E2E 测试到 Git Hooks 与 CI 工作流 本篇是 Datawhale vibe vib文档教程Vibe Coding示例工程从 Vibe Coding 到 Spec Coding在 easy-vibe 项目中用规范即代码重构 AI 编程工作流从 Vibe Coding 到 Spec Coding在 easy vibe 项目中用规范即代码重构 AI 编程工作流 「代码是意图的有损投影。」Code教程文档Vibe Coding 代码质量保障从评审、测试到自动化的完整实践指南Vibe Coding 代码质量保障从评审、测试到自动化的完整实践指南 本文是《Vibe Coding 零基础教程》经验技巧板块的第 06 篇原文档见 Vi文档教程知识库人工智能上一篇gRPC-rs 实战教程构建完整的 HelloWorld 客户端与服务器下一篇探秘ML Privacy Meter保护隐私的智能分析工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考