如何在Vitest单元测试中Mock Next.js的next/router?
针对Next.js中useRouter的Vitest Mock方案
基础Mock方案(Pages Router通用)
直接在测试文件顶部mock next/router,模拟useRouter返回的常用属性与方法:
import { vi } from 'vitest' import { useRouter } from 'next/router' // Mock整个next/router模块 vi.mock('next/router', () => ({ useRouter: vi.fn(() => ({ route: '/', pathname: '/', query: {}, asPath: '/', push: vi.fn().mockResolvedValue(true), replace: vi.fn().mockResolvedValue(true), back: vi.fn(), prefetch: vi.fn().mockResolvedValue(undefined), })), }))
测试用例中可动态修改返回值,比如模拟带query参数的路由:
test('组件接收query参数时正常渲染', () => { // 覆盖原有mock返回值 useRouter.mockReturnValue({ ...useRouter.mock.results[0].value, query: { id: '123' }, }) // 编写你的测试逻辑 })
App Router(Next.js 13+)适配方案
App Router的useRouter来自next/navigation,需针对性mock:
import { vi } from 'vitest' import { useRouter } from 'next/navigation' vi.mock('next/navigation', () => ({ useRouter: vi.fn(() => ({ push: vi.fn().mockResolvedValue(true), replace: vi.fn().mockResolvedValue(true), back: vi.fn(), forward: vi.fn(), refresh: vi.fn(), })), }))
全局Mock方案(减少重复代码)
在测试统一配置中设置全局mock,无需每个测试文件重复编写:
- 修改
vitest.config.ts指定setup文件:
import { defineConfig } from 'vitest/config' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], test: { environment: 'jsdom', setupFiles: './tests/setup.ts', }, })
- 在
tests/setup.ts中添加全局mock:
import { vi } from 'vitest' // Mock Pages Router的useRouter vi.mock('next/router', () => ({ useRouter: vi.fn(() => ({ route: '/', pathname: '/', query: {}, asPath: '/', push: vi.fn().mockResolvedValue(true), replace: vi.fn().mockResolvedValue(true), back: vi.fn(), })), })) // 同时Mock App Router的useRouter(按需添加) vi.mock('next/navigation', () => ({ useRouter: vi.fn(() => ({ push: vi.fn().mockResolvedValue(true), replace: vi.fn().mockResolvedValue(true), back: vi.fn(), })), }))
验证Router方法调用
测试中可断言路由方法是否被正确触发:
test('点击按钮跳转至详情页', async () => { render(<YourComponent />) // 触发跳转行为,比如点击按钮 await screen.getByRole('button', { name: '查看详情' }).click() // 断言push方法的调用参数 expect(useRouter().push).toHaveBeenCalledWith('/details/123') })
内容的提问来源于stack exchange,提问作者Mayank Kumar Chaudhari
相关产品推荐
相关产品推荐

