🚀 Next.js 全栈 E2E 测试:Playwright vs Cypress

214 阅读3分钟

写 Next.js 项目的人,最终都会走上这样一条路:
写前端 → 写 API → 写数据库 → 写测试 → 坐牢(调试半天测试不通过)。

为了不让自己凌晨三点还在盯着 npm test 发呆,我们需要掌握 E2E(End-to-End)测试

今天的主角是两位测试界的明星:

  • 🎭 Playwright —— 来自微软,擅长潜入浏览器底层搞事情。
  • 🌲 Cypress —— UI 界的老牌网红,界面友好,生态丰富。

接下来我们用 Next.js 做个全栈小例子,边写边比较。


🏗️ 1. 为什么要做 E2E 测试?

很多人觉得单元测试已经够了。

  • 单元测试:就像检查螺丝钉是不是拧紧。
  • E2E 测试:直接开上车,踩油门,看车会不会解体。

在全栈应用里,前端、后端、数据库之间任何一个环节崩了,用户体验就直接爆炸。E2E 测试帮我们模拟真实用户的操作,比如:

  1. 打开页面
  2. 填写表单
  3. 点击提交
  4. 检查后端是否真的保存成功

⚙️ 2. Next.js 项目准备

# 创建一个 Next.js 项目
npx create-next-app e2e-demo
cd e2e-demo

我们加一个简单的 Todo 功能:

pages/api/todos.js

let todos = [];

export default function handler(req, res) {
  if (req.method === "POST") {
    const todo = { id: Date.now(), text: req.body.text };
    todos.push(todo);
    return res.status(201).json(todo);
  }
  if (req.method === "GET") {
    return res.status(200).json(todos);
  }
}

pages/index.js

import { useState } from "react";

export default function Home() {
  const [todos, setTodos] = useState([]);
  const [text, setText] = useState("");

  async function addTodo() {
    const res = await fetch("/api/todos", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ text })
    });
    const newTodo = await res.json();
    setTodos([...todos, newTodo]);
    setText("");
  }

  return (
    <div>
      <h1>📝 Todo List</h1>
      <input
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="Write something..."
      />
      <button onClick={addTodo}>Add</button>
      <ul>
        {todos.map((t) => (
          <li key={t.id}>{t.text}</li>
        ))}
      </ul>
    </div>
  );
}

到这里,我们有了一个最小可测的全栈应用。


🎭 3. 用 Playwright 写 E2E 测试

安装:

npm install -D @playwright/test
npx playwright install

配置一个测试:

tests/todo.spec.js

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

test("用户可以添加一个 Todo", async ({ page }) => {
  await page.goto("http://localhost:3000");

  await page.fill("input", "买一杯咖啡");
  await page.click("button");

  const todo = await page.locator("li").last().innerText();
  expect(todo).toBe("买一杯咖啡");
});

运行:

npx playwright test

Playwright 的优势:

  • 多浏览器支持(Chromium、WebKit、Firefox)
  • 速度快,底层用浏览器协议直连
  • 内置截图、视频录制,调试时一目了然

🌲 4. 用 Cypress 写 E2E 测试

安装:

npm install -D cypress
npx cypress open

配置一个测试:

cypress/e2e/todo.cy.js

describe("Todo App", () => {
  it("用户可以添加一个 Todo", () => {
    cy.visit("http://localhost:3000");

    cy.get("input").type("写测试代码");
    cy.contains("Add").click();

    cy.get("li").last().should("have.text", "写测试代码");
  });
});

运行:

npx cypress open

Cypress 的优势:

  • 界面化调试,像玩小游戏一样点点点
  • 社区插件丰富
  • 断言语法自然(cy.get().should(...) 看起来比 expect 更口语化)

🥊 5. Playwright vs Cypress 对比

特性Playwright 🎭Cypress 🌲
浏览器支持Chromium / Firefox / WebKitChromium 系
速度⚡ 更快稍慢
调试体验截图 & 视频可视化界面强
API 风格类似 Jest链式操作,更口语化
CI/CD 适配强大成熟

一句话总结:

  • 如果你爱折腾底层,想要跨浏览器测试 → Playwright
  • 如果你想要上手快、调试方便 → Cypress

🌌 6. 收尾

测试不是“写完交差”的工作,它本质上是 给自己未来省头发
E2E 测试就像一台自动驾驶仪:

  • 让你放心上线
  • 让你半夜睡得更香
  • 让你的代码少点“玄学 Bug”

写代码的人和写测试的人,往往是同一个倒霉蛋。
但是相信我:测试写得好,未来的你会给现在的自己磕个头 🙇。


📌 作业

  • 用 Playwright 写一个“用户登录”E2E 测试。
  • 用 Cypress 写一个“数据持久化”测试(刷新后还能看到 Todo)。