如何为Next.js 12的Pages中间件编写测试?
Next.js 12 Pages中间件测试完整示例
针对你提供的中间件,下面是一套完整的测试方案,覆盖所有业务逻辑:
1. 安装测试依赖
首先安装必要的测试工具(假设你的项目是TypeScript):
npm install --save-dev jest @types/jest ts-jest @types/node
如果是JavaScript项目,去掉@types/jest和ts-jest,改用babel-jest适配。
2. 创建测试文件
在项目根目录创建middleware.test.ts(对应你的middleware.ts文件位置),内容如下:
import { middleware } from './middleware'; import { NextRequest, NextResponse } from 'next/server'; import * as jose from 'jose'; import { USER_ROLES } from './path-to-your-roles-file'; // 替换成你的USER_ROLES实际路径 // Mock外部依赖模块 jest.mock('jose', () => ({ decodeJwt: jest.fn(), })); // 模拟NextResponse的核心方法 jest.mock('next/server', () => ({ ...jest.requireActual('next/server'), NextResponse: { redirect: jest.fn(() => ({})), next: jest.fn(() => ({})), }, })); describe('Middleware Tests', () => { beforeEach(() => { // 每次测试前重置mock状态 jest.clearAllMocks(); }); // 测试/admin路径的权限控制逻辑 describe('/admin path authorization', () => { test('无auth token时重定向到首页', () => { const mockRequest = { nextUrl: { pathname: '/admin', searchParams: new URLSearchParams() }, cookies: { token: undefined }, url: 'http://localhost:3000/admin', } as unknown as NextRequest; middleware(mockRequest); expect(NextResponse.redirect).toHaveBeenCalledWith( new URL('/', mockRequest.url) ); }); test('有token但角色非admin时重定向到首页', () => { const mockToken = 'user-token'; (jose.decodeJwt as jest.Mock).mockReturnValue({ role: USER_ROLES.user }); const mockRequest = { nextUrl: { pathname: '/admin', searchParams: new URLSearchParams() }, cookies: { token: mockToken }, url: 'http://localhost:3000/admin', } as unknown as NextRequest; middleware(mockRequest); expect(NextResponse.redirect).toHaveBeenCalledWith( new URL('/', mockRequest.url) ); }); test('有admin角色token时允许正常访问', () => { const mockToken = 'admin-token'; (jose.decodeJwt as jest.Mock).mockReturnValue({ role: USER_ROLES.admin }); const mockRequest = { nextUrl: { pathname: '/admin', searchParams: new URLSearchParams() }, cookies: { token: mockToken }, url: 'http://localhost:3000/admin', } as unknown as NextRequest; middleware(mockRequest); expect(NextResponse.redirect).not.toHaveBeenCalled(); expect(NextResponse.next).toHaveBeenCalled(); }); test('token无效(解码返回非对象)时重定向到首页', () => { const mockToken = 'invalid-token'; (jose.decodeJwt as jest.Mock).mockReturnValue('invalid-payload'); const mockRequest = { nextUrl: { pathname: '/admin', searchParams: new URLSearchParams() }, cookies: { token: mockToken }, url: 'http://localhost:3000/admin', } as unknown as NextRequest; middleware(mockRequest); expect(NextResponse.redirect).toHaveBeenCalledWith( new URL('/', mockRequest.url) ); }); }); // 测试根路径的directToStore参数重定向逻辑 describe('/ path with directToStore param', () => { test('带directToStore参数时重定向到/store', () => { const mockRequest = { nextUrl: { pathname: '/', searchParams: new URLSearchParams('directToStore=true'), url: 'http://localhost:3000/', }, cookies: {}, } as unknown as NextRequest; middleware(mockRequest); expect(NextResponse.redirect).toHaveBeenCalledWith( new URL('/store', mockRequest.url) ); }); test('不带directToStore参数时正常放行', () => { const mockRequest = { nextUrl: { pathname: '/', searchParams: new URLSearchParams(), url: 'http://localhost:3000/', }, cookies: {}, } as unknown as NextRequest; middleware(mockRequest); expect(NextResponse.redirect).not.toHaveBeenCalled(); expect(NextResponse.next).toHaveBeenCalled(); }); test('非根路径带directToStore参数不触发重定向', () => { const mockRequest = { nextUrl: { pathname: '/other-page', searchParams: new URLSearchParams('directToStore=true'), url: 'http://localhost:3000/other-page', }, cookies: {}, } as unknown as NextRequest; middleware(mockRequest); expect(NextResponse.redirect).not.toHaveBeenCalled(); expect(NextResponse.next).toHaveBeenCalled(); }); }); });
3. 配置Jest(TypeScript项目)
在项目根目录创建jest.config.js:
module.exports = { preset: 'ts-jest', testEnvironment: 'node', moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx'], testMatch: ['**/*.test.ts', '**/*.test.js'], };
4. 运行测试
在package.json的scripts中添加测试命令:
{ "scripts": { "test": "jest" } }
然后执行测试:
npm run test
关键说明
- Mock外部依赖:通过
jest.mock隔离jose.decodeJwt和NextResponse,避免测试依赖真实JWT解码或HTTP环境 - 覆盖边界场景:除正常业务逻辑外,还测试了无效token、参数在非根路径等边缘情况
- 精准断言:针对不同场景,明确断言是否触发重定向或正常放行
内容的提问来源于stack exchange,提问作者zeckdude
相关产品推荐
相关产品推荐

