Next.js 教程系列(二十五)测试策略:单元、集成与端到端测试

前言

大家好,我是鲫小鱼。是一名不写前端代码的前端工程师,热衷于分享非前端的知识,带领切图仔逃离切图圈子,欢迎关注我,微信公众号:《鲫小鱼不正经》。欢迎点赞、收藏、关注,一键三连!!

第二十五章:测试策略:单元、集成与端到端测试

理论讲解

1. 测试金字塔与测试左移

  • 测试金字塔:底层是单元测试(数量最多、执行最快),中层是集成测试,顶层是端到端(E2E)测试(数量最少、最贴近用户)。合理分布测试类型,既保证质量又兼顾效率。
  • 测试左移:将测试环节前置到开发早期,越早发现问题,修复成本越低。TDD(测试驱动开发)和CI自动化测试是典型实践。
graph TD; A[需求/设计] --> B[开发] B --> C[单元测试] C --> D[集成测试] D --> E[E2E测试] E --> F[上线] C -.->|测试左移| B

测试左移理念:越早测试,越低成本。

2. 企业级Next.js测试体系全景

  • 单元测试(Unit Test):函数、组件、hooks,Mock依赖,快速定位问题。
  • 集成测试(Integration Test):模块/接口/服务协作,业务流程、API、Server Actions、RSC等。
  • 端到端测试(E2E Test):模拟真实用户,覆盖桌面与移动端,验证全链路。
  • 性能与安全测试:Lighthouse、Web Vitals、自动化安全扫描(如OWASP ZAP)。
  • 自动化测试与CI/CD:测试自动化、持续集成、质量门禁、自动回归、自动通知、自动回滚。
  • 团队协作与规范:测试用例评审、覆盖率门槛、知识库沉淀、分支策略。

3. 主流测试工具与选型

  • Jest:主流JS/TS单元测试框架,支持Mock、快照、覆盖率。
  • React Testing Library:以用户视角测试React组件。
  • Testing Library Hooks:测试自定义hooks。
  • Supertest:API/服务端接口测试。
  • Playwright/Cypress:E2E测试,支持多端、自动截图、视频回放。
  • msw(Mock Service Worker):前后端接口Mock,提升隔离性。
  • Lighthouse/Web Vitals:性能测试。
  • OWASP ZAP/Snyk:安全扫描。

详细代码示例

1. 单元测试:组件、函数、hooks

tsx 复制代码
// components/Button.tsx
export default function Button({ onClick, children }) {
  return <button onClick={onClick}>{children}</button>;
}
tsx 复制代码
// __tests__/Button.test.tsx
import { render, fireEvent } from '@testing-library/react';
import Button from '../components/Button';

test('点击按钮触发事件', () => {
  const handleClick = jest.fn();
  const { getByText } = render(<Button onClick={handleClick}>点我</Button>);
  fireEvent.click(getByText('点我'));
  expect(handleClick).toHaveBeenCalled();
});
ts 复制代码
// utils/math.ts
export function add(a, b) { return a + b; }
ts 复制代码
// __tests__/math.test.ts
import { add } from '../utils/math';
test('加法函数', () => {
  expect(add(1, 2)).toBe(3);
});
tsx 复制代码
// hooks/useMobile.ts
import { useEffect, useState } from 'react';
export function useMobile() {
  const [isMobile, setIsMobile] = useState(false);
  useEffect(() => {
    setIsMobile(window.innerWidth < 768);
  }, []);
  return isMobile;
}
tsx 复制代码
// __tests__/useMobile.test.tsx
import { renderHook } from '@testing-library/react';
import { useMobile } from '../hooks/useMobile';

test('移动端判断', () => {
  global.innerWidth = 375;
  const { result } = renderHook(() => useMobile());
  expect(result.current).toBe(true);
});

2. 集成测试:API Routes、Server Actions、RSC

ts 复制代码
// pages/api/hello.ts
export default function handler(req, res) {
  res.status(200).json({ message: 'Hello, Next.js!' });
}
ts 复制代码
// __tests__/api/hello.test.ts
import handler from '../../pages/api/hello';
import { createMocks } from 'node-mocks-http';

test('API 返回 Hello', async () => {
  const { req, res } = createMocks({ method: 'GET' });
  await handler(req, res);
  expect(res._getStatusCode()).toBe(200);
  expect(res._getJSONData()).toEqual({ message: 'Hello, Next.js!' });
});
ts 复制代码
// app/actions/submitFeedback.ts (Server Action)
'use server';
export async function submitFeedback(data) {
  if (!data.message) throw new Error('内容不能为空');
  // ...保存逻辑
  return { success: true };
}
ts 复制代码
// __tests__/actions/submitFeedback.test.ts
import { submitFeedback } from '../../app/actions/submitFeedback';
test('Server Action 校验', async () => {
  await expect(submitFeedback({ message: '' })).rejects.toThrow('内容不能为空');
  await expect(submitFeedback({ message: 'ok' })).resolves.toEqual({ success: true });
});
tsx 复制代码
// app/article/page.server.tsx (RSC)
export default async function ArticlePage({ params }) {
  const article = await fetch(`https://api.example.com/articles/${params.id}`).then(res => res.json());
  return <div>{article.title}</div>;
}
ts 复制代码
// __tests__/articlePage.test.tsx
import ArticlePage from '../app/article/page.server';
global.fetch = jest.fn(() => Promise.resolve({ json: () => ({ title: 'Test' }) }));
test('RSC 数据获取', async () => {
  const el = await ArticlePage({ params: { id: 1 } });
  expect(el.props.children).toBe('Test');
});

3. 端到端测试:桌面与移动端、性能与安全

js 复制代码
// e2e/home.spec.ts (Playwright)
import { test, expect } from '@playwright/test';

test('首页加载与交互', async ({ page }) => {
  await page.goto('http://localhost:3000');
  await expect(page.locator('text=欢迎')).toBeVisible();
  await page.setViewportSize({ width: 375, height: 667 }); // 模拟移动端
  await expect(page.locator('nav.mobile-menu')).toBeVisible();
});
js 复制代码
// cypress/e2e/login.cy.js
describe('登录流程', () => {
  it('用户可以登录', () => {
    cy.viewport('iphone-6');
    cy.visit('/login');
    cy.get('input[name=username]').type('testuser');
    cy.get('input[name=password]').type('123456');
    cy.get('button[type=submit]').click();
    cy.contains('欢迎,testuser').should('be.visible');
  });
});
js 复制代码
// e2e/performance.spec.ts (Lighthouse)
const { lighthouse, prepareAudit } = require('@cypress-audit/lighthouse');
describe('性能测试', () => {
  it('首页性能达标', () => {
    cy.visit('/');
    cy.lighthouse({ performance: 90 });
  });
});

4. Mock、隔离与安全测试

ts 复制代码
// __tests__/api/user.test.ts
import handler from '../../pages/api/user';
import { createMocks } from 'node-mocks-http';
import { getUser } from '../../services/user';
jest.mock('../../services/user');

test('API 返回用户信息', async () => {
  getUser.mockResolvedValue({ name: '鲫小鱼' });
  const { req, res } = createMocks({ method: 'GET' });
  await handler(req, res);
  expect(res._getJSONData()).toEqual({ name: '鲫小鱼' });
});
ts 复制代码
// __tests__/api/secure.test.ts
import handler from '../../pages/api/secure';
import { createMocks } from 'node-mocks-http';
test('API 拒绝未授权请求', async () => {
  const { req, res } = createMocks({ method: 'GET', headers: {} });
  await handler(req, res);
  expect(res._getStatusCode()).toBe(401);
});

5. CI/CD 集成与自动化

yaml 复制代码
# .github/workflows/test.yml
name: Test
on: [push, pull_request]
jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v2
      - name: Install deps
        run: npm ci
      - name: Run unit & integration tests
        run: npm test -- --coverage
      - name: Run E2E tests
        run: npx playwright test
      - name: Run Lighthouse
        run: npx lhci autorun
      - name: 安全扫描
        run: npx snyk test

实战项目:企业级全链路测试体系落地

1. 需求分析与目标

  • 组件、页面、API、Server Actions、RSC、端到端全链路测试。
  • 移动端适配、性能与安全测试。
  • 自动化、Mock、覆盖率统计、CI/CD集成、团队协作、质量门禁。

2. 目录结构

css 复制代码
components/
  Button.tsx
pages/
  api/
    hello.ts
    secure.ts
  login.tsx
app/
  actions/
    submitFeedback.ts
  article/
    page.server.tsx
hooks/
  useMobile.ts
utils/
  math.ts
services/
  user.ts
__tests__/
  Button.test.tsx
  math.test.ts
  hooks/
    useMobile.test.tsx
  api/
    hello.test.ts
    user.test.ts
    secure.test.ts
  actions/
    submitFeedback.test.ts
  articlePage.test.tsx
cypress/
  e2e/
    login.cy.js
    performance.spec.ts
e2e/
  home.spec.ts
.github/
  workflows/
    test.yml

3. 关键代码片段

  • 见上方详细代码示例。
  • Button、math、API、Server Actions、RSC、移动端、性能与安全全链路测试。
  • Mock服务端依赖,隔离外部影响。
  • test.yml集成单元、集成、E2E、性能、安全测试,自动化覆盖率统计。

4. 团队协作与CI质量门禁

  • Pull Request必须通过所有测试和覆盖率门槛。
  • 代码评审需关注测试用例质量。
  • 自动化通知、失败快照、自动回归。
  • 测试规范沉淀到团队知识库。

5. 项目亮点

  • 单元、集成、E2E、性能、安全全链路自动化测试。
  • Mock、隔离、覆盖率统计,保障质量。
  • CI/CD集成、自动回归、团队协作、质量门禁。
  • 代码结构清晰,易于维护和扩展。
  • 移动端适配、性能与安全测试全覆盖。

最佳实践

  • 单元测试覆盖所有核心函数、组件、hooks。
  • 集成测试覆盖API、服务端、Server Actions、RSC、业务流程。
  • 端到端测试覆盖关键用户路径、异常分支、移动端视图。
  • Mock外部依赖,提升测试隔离性和稳定性。
  • 自动化测试集成到CI/CD,强制通过后才能合并。
  • 覆盖率统计、趋势分析,定期review。
  • 团队协作:前端、后端、测试、产品协同,制定测试规范。
  • 性能与安全测试纳入CI流程。
  • 测试数据脱敏、权限隔离、合规审计。
  • 测试用例与需求、设计、Bug追踪系统联动。

常见问题与解决方案

Q1: 测试覆盖率低?

A: 制定覆盖率门槛,CI阶段强制检查,定期补齐核心代码测试。

Q2: E2E测试不稳定?

A: 用Mock、数据隔离、重试机制,避免依赖外部服务。

Q3: Mock数据与真实环境不一致?

A: 定期同步生产数据,或用msw动态Mock。

Q4: 测试执行慢?

A: 并行执行、只跑受影响用例、分阶段测试。

Q5: 团队协作难?

A: 制定测试规范、代码评审、自动化流程、知识库沉淀。

Q6: 如何保障安全与合规?

A: 测试数据脱敏、权限隔离、合规审计。

Q7: 移动端适配如何测试?

A: E2E测试中模拟不同设备视口,覆盖主流机型。

Q8: 性能与安全测试如何自动化?

A: 集成Lighthouse、Web Vitals、Snyk等工具到CI流程。


配图说明

graph TD; A[开发者提交代码] --> B[CI自动化测试] B --> C[单元测试] B --> D[集成测试] B --> E[E2E测试] B --> H[性能/安全测试] C --> F[覆盖率统计] D --> F E --> F H --> F F --> G[自动回归/质量门禁]

Next.js企业级自动化测试全流程示意图。

flowchart TB subgraph 团队协作 A1[需求评审] --> A2[测试用例设计] A2 --> A3[开发/实现] A3 --> A4[测试用例评审] A4 --> A5[自动化测试] A5 --> A6[CI质量门禁] A6 --> A7[知识库沉淀] end

团队协作与测试规范流程。


资源链接与完整源码

最后感谢阅读!欢迎关注我,微信公众号:《鲫小鱼不正经》。欢迎点赞、收藏、关注,一键三连!!

相关推荐
weixin_382395235 小时前
为小工厂量身打造:本地部署的物料管理系统带缺料计算
前端·制造
__zRainy__5 小时前
解决pnpm v10+不自动构建
前端·pnpm·工程化
猫猫不是喵喵.5 小时前
Vue3 Props 属性
前端·javascript·vue.js
醉城夜风~6 小时前
CSS元素显示模式(display)
前端·css
AI大模型-小华7 小时前
Codex 三方充值快速入门指南
java·前端·数据库·chatgpt·ai编程·codex·chatgpt pro
做前端的娜娜子9 小时前
同一链接实现 PC Web 与移动 H5 自适应
前端·掘金·金石计划
小帅不太帅9 小时前
架构没变、规模没变,DeepSeek V4 Flash 正式版凭什么暴涨 47 分?
前端·aigc·deepseek
jarvisuni10 小时前
DeepSeekFlash前端依旧拉垮,而且变慢了很多!
前端·javascript·算法
卷福同学11 小时前
AI编程出海第二步:验证关键词能否做站
前端·人工智能·后端