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

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


资源链接与完整源码

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

相关推荐
LLLLYYYRRRRRTT4 分钟前
MariaDB 数据库管理与web服务器
前端·数据库·mariadb
胡gh5 分钟前
什么是瀑布流?用大白话给你讲明白!
前端·javascript·面试
universe_0111 分钟前
day22|学习前端ts语言
前端·笔记
teeeeeeemo14 分钟前
一些js数组去重的实现算法
开发语言·前端·javascript·笔记·算法
Zz_waiting.16 分钟前
Javaweb - 14.1 - 前端工程化
前端·es6
掘金安东尼18 分钟前
前端周刊第426期(2025年8月4日–8月10日)
前端·javascript·面试
Abadbeginning18 分钟前
FastSoyAdmin导出excel报错‘latin-1‘ codec can‘t encode characters in position 41-54
前端·javascript·后端
ZXT20 分钟前
WebAssembly
前端
卢叁20 分钟前
Flutter开发环境安装指南
前端·flutter
curdcv_po38 分钟前
Three.js,闲谈3D——智慧XX
前端