Wishlist端到端测试实践:用Playwright自动化测试SvelteKit自托管应用的完整指南

【免费下载链接】wishlist

Wishlist is a self-hosted wishlist application that you can share with your friends and family. You no longer have to wonder what to get your family for the holidays, simply check their wishlist and claim any available item!

项目地址: https://gitcode.com/gh_mirrors/wish/wishlist
点击查看 免费下载

Wishlist 是一个可自托管的愿望清单应用(self-hosted wishlist):家人和朋友各自维护愿望清单,有人买下心仪礼物时直接"认领"即可。为了守住这些核心功能,项目用 Playwright 搭建了一套完整的端到端测试(E2E 测试)体系:一键用 Docker 拉起应用、自动完成初始化向导、再模拟真实用户在浏览器里点击、填写表单、多账号交互。本文带你拆解这套测试是如何组织的,方便你在自己的 SvelteKit 项目中复刻。

为什么自托管应用要做端到端测试?

Wishlist 愿望清单应用桌面端主界面,显示成员清单与认领进度

Wishlist 是一个典型的"多用户 + 自托管" Web 应用(SvelteKit + Prisma + SQLite)。这类应用有几个特点,决定了端到端测试价值很大:

  • 用户路径长:从注册、初始化向导、创建清单,到添加愿望、认领、购买,任何一环坏掉都会让最终用户"用不了";
  • 多人协作:一个用户创建愿望,另一个用户认领,纯接口测试很难覆盖真实的页面交互;
  • 自托管部署:用户往往通过 Docker 部署,在容器里把整个流程跑一遍,比单纯检查 API 更贴近真实环境。

Playwright 正好补齐这些缺口:它直接驱动 Chromium,能访问真实页面、真实表单和 Toast 提示,还能在失败重试时自动录制 trace 方便排查。

测试目录结构一览:tests/ 下的四层组织

所有测试代码集中在 tests/ 目录,约 3000 行,分成清晰的四层:

目录职责示例
tests/ui/端到端测试用例(spec 文件)list.spec.ts、claims.spec.ts
tests/pageObjects/页面级封装(Page Object)list.page.ts、setup-wizard.page.ts
tests/modules/组件级封装(弹窗、Toast、卡片等)toast.ts、item-card.ts
tests/fixtures/自定义测试夹具(多用户场景)index.ts

此外还有两个基础设施文件:

tests/ui/ 下的用例按功能域拆分,覆盖了应用的主要业务:

  • auth.spec.ts:登录、注册等认证流程;
  • list.spec.ts / lists.spec.ts:愿望清单的创建、公开共享、添加愿望(支持 URL 自动抓取商品信息);
  • claims.spec.ts:单个认领、部分数量认领、无限量认领等核心"认领"场景;
  • list-managers.spec.ts:清单管理者的权限管理;
  • suggestions.spec.ts:商品建议(suggestions)功能。

尚未覆盖的功能则记录在 tests/TODO.md 中,用勾选清单的方式规划测试路线——这是一个很好的工程习惯:测试覆盖率本身也应该是可追踪的任务。

零配置启动环境:webServer 与 Docker

在 playwright.config.ts 中,webServer 配置让 Playwright 在测试开始前自动构建并启动应用,开发者无需手动执行任何 Docker 命令:

webServer: {
    command:
        "docker compose -f tests/docker/docker-compose.yaml up --remove-orphans --renew-anon-volumes --force-recreate",
    url: "http://localhost:3280",
    timeout: 60000 * 5,
    reuseExistingServer: false
}

几个值得借鉴的细节:

  • --renew-anon-volumes:每次运行都刷新匿名卷,保证每个测试会话都是全新的数据库,测试之间互不污染;
  • reuseExistingServer: false:强制使用新启动的服务,避免残留状态干扰;
  • 超时给到 5 分钟,因为首次运行需要构建镜像。

配置文件还有几个稳健性设置:本地允许 fullyParallel: true 并行执行,CI 上改为单 worker 串行(workers: process.env.CI ? 1 : undefined);失败重试本地 1 次、CI 2 次(retries: process.env.CI ? 2 : 1);并用 forbidOnly: !!process.env.CI 防止有人把 test.only 忘在代码里上了 CI。

全局初始化:自动跑完安装向导

Wishlist 首次启动会进入 Setup Wizard(创建管理员、全局设置、邀请用户)。如果每次测试都手工操作一遍会很麻烦,global.setup.ts 用 Playwright 把整个过程自动化了:

setup("setup wizard", async ({ page }) => {
    await page.goto("/");
    if (new URL(page.url()).pathname === "/login") {
        return; // 已有管理员,说明环境已初始化
    }
    const setupWizardPage = new SetupWizardPage(page);
    await setupWizardPage.getStarted();
    await setupWizardPage.completeSteps();
    // 把管理员登录态存下来,供后续测试复用
    await page.context().storageState({ path: adminAuthFile });
});

两个关键点:

  1. 幂等设计:如果访问首页被重定向到 /login,说明已初始化,直接跳过;
  2. storageState 复用:通过 SetupWizardPage 完成向导后,把管理员的浏览器登录态保存到 playwright/.auth/admin.json(路径定义在 constants.ts),后续需要管理员权限的测试直接加载这个文件即可,不用反复登录。

在 projects 配置里,chromium 项目通过 dependencies: ["setup"] 声明依赖,保证 setup 先于所有用例执行。

自定义 fixtures:一次搞定多用户场景

认领愿望天然需要两个以上用户(一个创建、一个认领)。tests/fixtures/index.ts 基于 Playwright 的 base.extend() 扩展出一组业务夹具,让测试代码几乎不用关心"造用户"的细节:

  • userData / page:通过 API 创建一个新用户,并自动登录拿到该用户的页面;
  • additionalUserData / additionalPage:在同组内再建一个用户,专用于"认领"类双人场景;
  • anonymousPage:干净的匿名页面,用于验证未登录访客看到的内容(比如公开清单是否可见);
  • userFactory:一个 UserFactory 类,需要多少用户就 createUserPage() 多少次,用完自动关闭所有浏览器上下文。

其中 util.ts 里的 createUser 演示了 Playwright "API + UI 混合测试"的思路:用 request.put("/api/users") 这类 API 调用快速准备数据(创建用户、建组、授权),再用 UI 操作验证真实交互——准备数据走 API,验证行为走浏览器,既快又稳。

一条典型的认领测试(摘自 claims.spec.ts):

test("claim single", async ({ page: ownerPage, userData: owner, additionalPage: claimerPage }) => {
    await test.step("create item", async () => {
        // owner 创建一个愿望
        const createItemPage = await ownerListPage.createItem();
        await createItemPage.getForm().then((f) => f.fillName(randomString()));
        await createItemPage.create();
    });
    await test.step("user claims item", async () => {
        // 另一个用户打开清单并认领
        await item.claimSingle();
    });
    await test.step("list card shows none available", async () => {
        // 清单卡片显示 0 个可认领
        await card.assertAvailableCount(0);
    });
});
`

test.step 把用例切成语义化步骤,报告里一眼就能看出失败发生在哪一步。

页面封装:Page Object 让用例像"写文档"

Wishlist 的添加愿望表单支持名称、价格、数量、备注、多清单同步等大量字段,如果每个用例都直接写 page.locator("..."),一旦界面微调就会大面积失败。项目的做法是两层封装:

页面层(tests/pageObjects/):以 BasePage 为基类,at() 断言"我确实到了这个页面",goto() 则"跳转 + 断言"一步完成。子类如 ListPage 把按钮、标题、容器都缓存为 Locator,对外只暴露 createItem()、manage()、getItemAt(0) 这类业务语义方法。

组件层(tests/modules/):弹窗(modal.ts)、Toast 提示(toast.ts)、愿望卡片(item-card.ts)等复用性 UI 片段单独封装。

Wishlist 添加愿望清单表单,包含名称价格数量等字段

封装的收益在 list.spec.ts 里体现得很明显——"创建一个完整愿望"这条用例读起来几乎就是一段操作说明:

test("create full item", async ({ page, userData }) => {
    const listsPage = new ListsPage(page);
    await listsPage.goto();
    const list = await listsPage.getListAt(0);
    const listPage = await list.click();
    const createItemPage = await listPage.createItem();
    await createItemPage
        .getForm()
        .then((f) => f.fillName(itemData.name))
        .then((f) => f.fillPrice(itemData.rawPrice))
        .then((f) => f.fillNote(itemData.notes));
    await createItemPage.create();
    await new Toast(page).waitForToastWithText("Item created");
    // 再逐项断言名称、价格、数量、备注、创建人……
});

整段代码没有任何硬编码的 CSS 选择器,断言也全部委托给 item-card 模块(如 assertPrice、assertNotes)。界面重构时只需改封装层,用例基本不动。

运行测试:三条命令就够了

package.json 里定义了三个脚本,覆盖了从安装到运行的完整链路:

pnpm test:setup   # playwright install --with-deps chromium,安装浏览器及系统依赖
pnpm test:build   # 构建测试专用 Docker 镜像 wishlist:test
pnpm test         # 运行 playwright test

pnpm test 会触发 playwright.config.ts 中的 webServer,自动起服务 → 跑 setup 向导 → 并行执行全部用例。配置中 reporter: "html" 指定了 HTML 报告器,测试结束后运行 npx playwright show-report 即可在浏览器里查看详细结果;再配合 trace: "on-first-retry",失败重试时会自动保存 trace 文件,可以逐帧回放页面状态来定位问题。

小结:可直接搬走的 5 个实践

  1. Docker 即环境:用 webServer + 专用 docker-compose 文件,让"启动一个干净的应用"变成测试的一部分;
  2. setup 依赖前置:用 dependencies 让全局初始化(如安装向导)自动跑在所有用例之前,并把登录态存为 storageState 复用;
  3. fixtures 封装多用户:自托管协作类应用一定要提前设计"多账号夹具",双人、多人场景才不会重复写登录逻辑;
  4. API 备数据、UI 验行为:page.request 快速造数据,页面操作只留给真正需要验证的交互;
  5. Page Object + 组件模块分层:业务语义方法与 Locator 细节隔离,用例保持"文档级"可读性。

这套 Playwright 端到端测试为 Wishlist 的登录、清单、认领、建议等核心链路提供了回归保障,也让贡献者可以放心重构界面。如果你也在维护 SvelteKit 或任何自托管 Web 应用,不妨照着 tests/ 目录的结构搭起自己的第一版 E2E 测试——从 tests/TODO.md 那种勾选清单开始,把"要测什么"先写下来,剩下的交给浏览器自动化。

【免费下载链接】wishlist

Wishlist is a self-hosted wishlist application that you can share with your friends and family. You no longer have to wonder what to get your family for the holidays, simply check their wishlist and claim any available item!

项目地址: https://gitcode.com/gh_mirrors/wish/wishlist
点击查看 免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值