00 / 00

测试

01MVP 代码模板的测试覆盖范围、运行命令和扩展建议

这套模板的测试目标是守住最容易改坏、也最影响用户信任的地方:配置、路由、权限边界、登录注册、支付入口、上传安全、共享包和公开页面。

测试怎么选? Web 测试和 Package 测试属于单元测试,不启动浏览器,跑得快,适合验证逻辑和结构。浏览器 E2E 会启动真实页面,适合验证登录、跳转、移动端菜单和关键公开页。日常开发优先跑单元测试,发布前一定跑关键 E2E。

项目当前有三层测试:

  • Web / Package 单元测试:快速验证配置、路由、文案、API 边界和共享包逻辑。
  • 浏览器 E2E:用 Playwright 打开真实页面,监听页面错误,验证公开流程、匿名保护边界和必要的登录态流程。
  • 生产 smoke:先 build,再用生产 Node server 启动页面,确认生产产物、CSP、静态资源、健康检查和登录路由都能工作。

推荐测试流程

日常开发按风险分层执行:

单元和契约测试:不依赖 seed 账号,不复用真实登录态。用 fixture、mock 或测试内 setup 验证权限函数、API contract、路由边界和纯逻辑。

vpr @01mvp/web#test
vpr @01mvp/api#test
vpr @01mvp/auth#test

匿名 E2E / smoke:不需要 seed 账号。验证首页、登录页、注册页、未登录访问受保护路由会跳登录、移动端菜单和系统端点。

vpr @01mvp/product#test:e2e:public

已登录 E2E:只在本地或可丢弃数据库上执行。01MVP 默认使用本机 postgresql://jackiexiao@localhost:5432/01mvp,你也可以用 PLAYWRIGHT_DATABASE_URL 覆盖。先应用迁移,再由 Playwright auth-setup 项目运行 db:seed、登录测试管理员并保存认证状态,后续登录态用例复用这个状态。

vpr @01mvp/product#db:migrate
vpr @01mvp/product#test:e2e:auth

完整本地 E2E:同时运行匿名、移动端和已登录浏览器测试。它会触发 auth-setup,因此需要本地或可丢弃数据库。

vpr @01mvp/product#test:e2e

本地生产 smoke:默认在本地构建并启动生产产物,不访问真实线上环境。

vpr @01mvp/product#test:e2e:production

预发或线上 smoke:只有显式传入 PLAYWRIGHT_BASE_URL 时才会访问指定环境。预发可以使用专用测试账号;真实生产只做低风险检查,不运行 db:seed,不做破坏性后台操作。

PLAYWRIGHT_BASE_URL=https://staging.example.com vpr @01mvp/product#test:e2e:production

登录态 E2E 可以复用一个测试管理员来验证只读页面和基础导航。如果测试会修改服务端状态,例如改资料、改权限、删数据或创建订单,应使用独立测试账号、测试内数据前缀,或在测试结束后清理数据。

运行命令

Web 测试 -- 快速验证配置和路由

vpr @01mvp/web#test

Package 测试 -- 验证共享包逻辑

vpr @01mvp/api#test
vpr @01mvp/auth#test

覆盖率 -- 用 package-local 测试入口统计当前 package 的 Vitest 覆盖率

vpr @01mvp/web#test --coverage

生产构建 -- 确认代码能正常编译

vpr @01mvp/product#build

浏览器 E2E -- 验证关键页面和用户流程

vpr @01mvp/product#test:e2e:public
vpr @01mvp/product#test:e2e:auth
vpr @01mvp/product#test:e2e

生产 smoke -- 构建后启动生产服务再验证

vpr @01mvp/product#test:e2e:production

改动了 01MVP package 层代码时,建议跑产品级检查,它会通过 Vite Plus 调度 01MVP 下各 workspace 的类型检查和测试:

vpr @01mvp/product#type-check
vpr @01mvp/product#test

当前覆盖范围

层级命令已覆盖内容
Web 测试vpr @01mvp/web#test模板配置、路由覆盖、迁移边界、公开页面文案和 API 边界
Web 覆盖率vpr @01mvp/web#test --coverage当前 Web app 的 Vitest 覆盖率,不启动浏览器
Package 测试vpr @01mvp/api#testvpr @01mvp/auth#test认证、权限、支付、积分、存储、配置、AI、日志、UI 基础组件
01MVP 产品测试vpr @01mvp/product#test通过 Vite Plus 运行 01MVP 产品内已定义的测试脚本
公开浏览器 E2Evpr @01mvp/product#test:e2e:public首页、登录注册、OAuth 可见性、手机号/邮箱入口、受保护路由跳登录、Drop 登录恢复、移动端菜单、公开长尾页面、系统端点
已登录浏览器 E2Evpr @01mvp/product#test:e2e:authPlaywright setup 登录 seed 管理员后访问 /dashboard/dashboard/security/admin
完整浏览器 E2Evpr @01mvp/product#test:e2e同时运行公开、移动端和已登录 E2E
生产 smokevpr @01mvp/product#test:e2e:production生产构建、Node server 启动、SSR、CSP nonce、静态资源、健康检查、登录路由

这几个层级是互补的:Vitest 跑得快,适合守住模板结构和共享包;浏览器 E2E 适合确认页面真的能打开、点击后真的能跳转;生产 smoke 用来抓 dev server 里看不到的构建和运行时问题。

Web 测试示例

import { readFileSync } from "node:fs";
import { describe, expect, it } from "vite-plus/test";

describe("route parity", () => {
  it("keeps dashboard route represented", () => {
    const source = readFileSync("src/routeTree.gen.ts", "utf8");
    expect(source).toContain("/dashboard");
  });
});

这类测试放在 products/01mvp/apps/web/src/__tests__/*.test.ts。不需要真实浏览器的结构、边界和纯逻辑测试,都优先放这里。

Playwright 测试示例

import { expect, test } from "@playwright/test";

test("dashboard 未登录跳转登录页", async ({ page }) => {
  await page.goto("/dashboard", { waitUntil: "networkidle" });
  await expect(page).toHaveURL(/\/auth\/login/);
  expect(new URL(page.url()).searchParams.get("redirect")).toBe("/dashboard");
});

这类测试放在 products/01mvp/apps/web/__e2e__/**/*.spec.ts。本地调试可以进入 products/01mvp/apps/web 后运行:

vp exec playwright test --debug

当前 E2E 默认使用隔离端口 7010,避免误用你手动启动在 7001 的开发服务。如果要指定地址,可以设置:

PLAYWRIGHT_BASE_URL=http://localhost:7001 vpr @01mvp/product#test:e2e:public

移动端用 .mobile.spec.ts 文件,登录态用 .auth.spec.ts 文件,生产 smoke 用 .production.spec.ts 文件。

登录态 E2E 的认证状态

需要登录态的 E2E 推荐使用 Playwright setup project:

  1. auth-setup 项目运行 vpr @01mvp/product#db:seed 准备测试管理员。
  2. auth-setup 通过登录页完成一次真实邮箱密码登录。
  3. 登录成功后把 storageState 保存到 test-results/.auth/admin.json
  4. authenticated-chromium 项目通过 storageState 启动,直接访问 /dashboard/dashboard/security/admin 等页面。

db:seed 不需要改名。它负责准备数据库数据;Playwright 的 auth-setup 负责准备浏览器登录态。认证状态文件包含 cookie 和会话信息,不能提交到 Git;当前写入已忽略的 test-results/.auth/,只作为本次测试产物。

如果要让 auth E2E 使用指定数据库,可以传入:

PLAYWRIGHT_DATABASE_URL=postgresql://jackiexiao@localhost:5432/01mvp vpr @01mvp/product#test:e2e:auth

如果目标是可丢弃的远程开发库,必须显式放行:

PLAYWRIGHT_DB_SEED_ALLOW_REMOTE=true \
PLAYWRIGHT_DATABASE_URL=postgresql://postgres:[email protected]:5432/01mvp-start \
PLAYWRIGHT_BASE_URL=https://staging.example.com \
vpr @01mvp/product#test:e2e:auth

如果仓库里同时有 OneSay,OneSay 的本地浏览器测试走 Wrangler D1:Playwright 会先执行 @onesay/db 的本地 D1 migration,再用 Wrangler dev 启动应用,auth-setup 会把测试管理员写入 Wrangler 的本地 D1 状态,不会调用 Postgres seed。

生产 smoke 的目标环境

vpr @01mvp/product#test:e2e:production 默认会执行本地产品 build 和 start,然后访问 http://localhost:7011。这类测试用于确认生产构建产物能启动、SSR 正常、CSP nonce 正常、静态资源能访问、/api/health 正常、登录路由可用。

如果设置了 PLAYWRIGHT_BASE_URL,Playwright 不会启动本地服务,而是直接访问这个地址:

PLAYWRIGHT_BASE_URL=https://example.com vpr @01mvp/product#test:e2e:production

真实生产 smoke 应保持只读、低风险:检查首页、登录页、健康检查、静态资源和未登录保护边界即可。需要写数据或管理员权限的流程放在本地、CI 临时库或预发环境。

什么时候补测试

不确定要不要写测试?简单原则:涉及钱、权限、登录的改动,必须有测试。其他改动建议有测试。

下面这些改动建议同步补测试:

  • 新增 Hono route、oRPC procedure、TanStack route guard 或鉴权中间件
  • 修改支付、积分、权限、上传、登录注册、数据库 schema
  • 调整 packages/* 中会被多个 app 复用的逻辑
  • 修改公开文档路由、定价页、登录页、设置页、后台页
  • 修复线上 bug(先写能复现问题的测试,再改实现)

登录注册属于必须重点保护的流程。至少要覆盖:

  • /auth/login 能打开,页面没有 runtime error。
  • /api/rpc/auth/capabilities 正常返回,Google / GitHub 按钮按配置可见。
  • 手机号登录、邮箱登录、注册入口能进入对应表单。
  • 未登录访问 /dashboard/ai/admin 会跳到登录页,并保留安全的 redirect。
  • 移动端菜单能打开,并能进入登录页。

测试放哪里

你要测什么放在哪里
不需要浏览器的 app 结构、配置、路由边界products/01mvp/apps/web/src/__tests__/*.test.ts
共享 package 的纯函数、provider、service对应 packages/<name>/src/**/*.test.ts
用户可见页面、跳转、移动端渲染products/01mvp/apps/web/__e2e__/**/*.spec.ts
只在移动端跑的浏览器流程products/01mvp/apps/web/__e2e__/**/*.mobile.spec.ts
需要登录态的浏览器流程products/01mvp/apps/web/__e2e__/**/*.auth.spec.ts
需要先 build 再验证的生产流程products/01mvp/apps/web/__e2e__/**/*.production.spec.ts
文档导航是否能打开优先放入浏览器 E2E 或生产 smoke
需要真实外部服务的流程放到预发环境脚本或单独 E2E,避免污染默认本地测试

默认原则:越靠近底层逻辑,测试越小;越靠近用户流程,测试越像真实浏览器操作。

这篇文档有问题?