跳至主要内容

Playwright 测试指南:Next.js 应用程序的 E2E 测试

使用 Playwright 为 Next.js 应用程序编写可靠的端到端测试。完整的指南涵盖设置、页面对象、身份验证测试、视觉回归和 CI/CD 集成。

E
ECOSIRE Research and Development Team
|2026年3月5日4 分钟阅读648 字数|

端到端测试捕获单元测试遗漏的错误 - 身份验证流程中断、表单提交失败、导航错误和跨浏览器渲染问题。 Playwright 由 Microsoft 构建,凭借其多浏览器支持、自动等待功能和强大的调试工具,已成为领先的 E2E 测试框架。

端到端测试捕获单元测试遗漏的错误 - 身份验证流程中断、表单提交失败、导航错误和跨浏览器渲染问题。 Playwright 由 Microsoft 构建,凭借其多浏览器支持、自动等待功能和强大的调试工具,已成为领先的 E2E 测试框架。

要点

  • Playwright 测试通过单个测试套件跨 Chromium、Firefox 和 WebKit 运行
  • 自动等待通过在交互之前等待元素可操作来消除不稳定的测试
  • 身份验证状态可以在测试之间共享,以加快执行速度
  • CI 与 GitHub Actions 集成提供对每个拉取请求的自动化测试

项目设置

安装

npm init playwright@latest

这将安装 Playwright、创建配置文件并下载浏览器二进制文件。对于 Next.js 项目,将 Web 服务器配置为自动启动:

// playwright.config.ts
import { defineConfig } from "@playwright/test";

export default defineConfig({
  testDir: "./e2e",
  timeout: 30000,
  retries: process.env.CI ? 2 : 0,
  use: {
    baseURL: "http://localhost:3000",
    screenshot: "only-on-failure",
    trace: "on-first-retry",
  },
  webServer: {
    command: "npm run dev",
    port: 3000,
    reuseExistingServer: !process.env.CI,
  },
  projects: [
    { name: "chromium", use: { browserName: "chromium" } },
    { name: "firefox", use: { browserName: "firefox" } },
    { name: "webkit", use: { browserName: "webkit" } },
  ],
});

编写你的第一个测试

基本导航测试

// e2e/navigation.spec.ts
import { test, expect } from "@playwright/test";

test("home page loads and displays title", async ({ page }) => {
  await page.goto("/");
  await expect(page).toHaveTitle(/ECOSIRE/);
  await expect(page.getByRole("heading", { level: 1 })).toBeVisible();
});

test("navigation to services page", async ({ page }) => {
  await page.goto("/");
  await page.getByRole("link", { name: "Services" }).click();
  await expect(page).toHaveURL(/services/);
  await expect(page.getByRole("heading", { name: /Services/ })).toBeVisible();
});

表单提交测试

test("contact form submission", async ({ page }) => {
  await page.goto("/contact");

  await page.getByLabel("Name").fill("Test User");
  await page.getByLabel("Email").fill("test@example.com");
  await page.getByLabel("Message").fill("This is a test message");
  await page.getByRole("button", { name: "Send" }).click();

  await expect(page.getByText("Message sent")).toBeVisible();
});

页面对象模式

将页面交互封装在页面对象中以实现可维护性:

// e2e/pages/login.page.ts
import { Page, expect } from "@playwright/test";

export class LoginPage {
  constructor(private page: Page) {}

  async goto() {
    await this.page.goto("/login");
  }

  async login(email: string, password: string) {
    await this.page.getByLabel("Email").fill(email);
    await this.page.getByLabel("Password").fill(password);
    await this.page.getByRole("button", { name: "Sign In" }).click();
  }

  async expectLoggedIn() {
    await expect(this.page).toHaveURL(/dashboard/);
  }

  async expectError(message: string) {
    await expect(this.page.getByText(message)).toBeVisible();
  }
}

在测试中使用:

test("successful login", async ({ page }) => {
  const loginPage = new LoginPage(page);
  await loginPage.goto();
  await loginPage.login("user@example.com", "password123");
  await loginPage.expectLoggedIn();
});

认证测试

共享身份验证状态

通过保存身份验证状态避免在每次测试之前登录:

// e2e/auth.setup.ts
import { test as setup } from "@playwright/test";

setup("authenticate", async ({ page }) => {
  await page.goto("/login");
  await page.getByLabel("Email").fill("admin@example.com");
  await page.getByLabel("Password").fill("admin-password");
  await page.getByRole("button", { name: "Sign In" }).click();
  await page.waitForURL("/dashboard");

  // Save authentication state
  await page.context().storageState({ path: ".auth/user.json" });
});

在 playwright.config.ts 中配置:

projects: [
  { name: "setup", testMatch: /auth\.setup\.ts/ },
  {
    name: "authenticated",
    dependencies: ["setup"],
    use: { storageState: ".auth/user.json" },
  },
],

视觉回归测试

通过屏幕截图比较捕捉意外的视觉变化:

test("home page visual regression", async ({ page }) => {
  await page.goto("/");
  await expect(page).toHaveScreenshot("home-page.png", {
    maxDiffPixelRatio: 0.01,
  });
});

第一次运行时,Playwright 会保存基线屏幕截图。后续运行将与基线进行比较,如果差异超过阈值,则失败。当有意更改时,使用 --update-snapshots 更新基线。


API 模拟

用于确定性测试的模拟 API 响应:

test("displays products from API", async ({ page }) => {
  await page.route("/api/products", async (route) => {
    await route.fulfill({
      status: 200,
      contentType: "application/json",
      body: JSON.stringify([
        { id: 1, name: "Product A", price: 29.99 },
        { id: 2, name: "Product B", price: 49.99 },
      ]),
    });
  });

  await page.goto("/products");
  await expect(page.getByText("Product A")).toBeVisible();
  await expect(page.getByText("Product B")).toBeVisible();
});

CI/CD 集成

GitHub 操作

name: E2E Tests
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npm ci
      - run: npx playwright install --with-deps
      - run: npx playwright test
      - uses: actions/upload-artifact@v4
        if: failure()
        with:
          name: playwright-report
          path: playwright-report/

失败的测试报告作为调试工件上传,包括屏幕截图、跟踪和视频记录。


调试工具

  • 剧作家检查器:使用 --debug 标志运行测试以逐步执行
  • 跟踪查看器:打开跟踪以查看每个操作、网络请求和 DOM 快照
  • Codegen:运行npx playwright codegen来记录浏览器交互并生成测试代码
  • VS Code 扩展:使用点击定位元素直接从 VS Code 运行和调试测试

最佳实践

  1. 使用基于角色的选择器:优先使用 getByRole、getByLabel、getByText 而不是 CSS 选择器
  2. 避免硬编码等待:信任自动等待而不是 page.waitForTimeout()
  3. 隔离测试:每个测试应该是独立的,不依赖于其他测试
  4. 测试用户流程,而不是实现:关注用户做什么,而不是代码如何工作
  5. Keep tests fast: Use API shortcuts for setup (create test data via API, not UI)
  6. 在 CI 中运行:每个 Pull 请求在合并之前都应该通过 E2E 测试

常见问题

问:Playwright 与 Cypress 相比如何?

Playwright 本身支持多种浏览器(Chromium、Firefox、WebKit),而 Cypress 主要针对 Chromium。 Playwright 的并行执行速度更快,并且可以处理多个选项卡/窗口。 Cypress 的学习曲线稍微容易一些,并且时间旅行调试能力更强。

问:我们如何处理不稳定的测试?

剧作家的自动等待消除了大多数不稳定的情况。对于剩余问题:使用 Web 优先断言(期望定位器),避免测试依赖于时间的行为,并在 CI 中配置重试。跟踪报告有助于确定间歇性故障的根本原因。

问:我们应该测试每个页面吗?

专注于关键用户流程:身份验证、核心业务工作流程、表单提交和支付流程。并非每个页面都需要 E2E 测试——使用单元和集成测试来实现组件级覆盖。

问:E2E 测试需要多长时间?

个别测试应在 30 秒内完成。中型应用程序的完整套件(50-100 次测试)应在并行执行的情况下在 10 分钟内运行。


下一步是什么

使用 Playwright 进行 E2E 测试可以确保您的应用程序从用户角度正确运行。从关键流程开始,并随着应用程序的增长扩大覆盖范围。

联系 ECOSIRE 获取测试自动化帮助,或探索我们的 Odoo 实施服务 进行有质量保证的 ERP 部署。


由 ECOSIRE 发布——帮助企业利用企业软件解决方案进行扩展。

E

作者

ECOSIRE Team

Technical Writing

The ECOSIRE technical writing team covers Odoo ERP, Shopify eCommerce, AI agents, Power BI analytics, GoHighLevel automation, and enterprise software best practices. Our guides help businesses make informed technology decisions.

ECOSIRE

与 ECOSIRE 一起发展您的业务

跨 ERP、电子商务、人工智能、分析和自动化的企业解决方案。