Nuxt 3集成测试中如何正确打桩服务端auth中间件
Nuxt服务端认证中间件集成测试打桩实现方案
核心失效原因
你之前尝试的两种方案不生效,本质是因为Nuxt(Nitro)启动时会自动扫描server/middleware目录,把所有中间件提前注册到请求处理链中,初始化完成后再修改配置指向、或者在测试用例里调用vi.mock(),都没法替换已经注册完成的原始中间件逻辑。
可落地方案
方案1:高优先级Nitro插件覆写上下文(推荐,无业务代码侵入)
不需要修改原始认证中间件代码,通过测试环境加载优先级更高的Nitro插件,直接覆写请求上下文中的auth字段,绕开原始AD JWT校验逻辑:
- 新建测试专用mock文件
tests/mocks/auth-mock.ts,写入以下逻辑:
import { ref } from 'vue' import type { Ref } from '@vue/runtime-dom' const NOT_LOGGED_IN = null export const isLoggedIn: Ref<{ email: string, name: string } | null> = ref(NOT_LOGGED_IN) export const useAuthUserMock = () => ({ resetAuthMockUser: () => { isLoggedIn.value = NOT_LOGGED_IN }, setAuthMockUser: (email = 'user.mock@email.com', name = 'User Mock') => { isLoggedIn.value = { email, name } } }) export default defineNitroPlugin((nitroApp) => { // 注册请求钩子,每个请求进入时赋值auth上下文 nitroApp.hooks.hook('request', (event) => { // 如果原始中间件会在无JWT时直接抛401,把钩子改成'request:early'提前执行即可 event.context.auth = isLoggedIn.value }) })
- 在测试配置中引入该插件,如果你用
@nuxt/test-utils做集成测试,直接在测试的setup配置中添加Nitro插件配置即可,测试环境下插件会自动加载。
方案2:Vitest路径别名强制替换(适配所有场景)
如果插件方案不适用,可以直接在Vitest配置层把原始中间件的引用路径映射到mock文件,从模块解析层面替换原始逻辑:
- 把你写的打桩版中间件代码存到
tests/mocks/auth-middleware-stub.ts - 修改
vitest.config.ts,添加路径别名规则:
import { defineConfig } from 'vitest/config' import { fileURLToPath } from 'node:url' export default defineConfig({ resolve: { alias: { // 把原始中间件路径直接映射到mock文件 '~/server/middleware/auth': fileURLToPath(new URL('./tests/mocks/auth-middleware-stub.ts', import.meta.url)) } }, // 其他原有测试配置不变 })
这个方案会在模块加载阶段直接替换所有对原始auth中间件的引用,不会出现mock不生效的问题。
测试用例写法参考
用例执行前记得重置登录状态,避免多用例之间状态污染:
// 导入mock状态控制方法 import { useAuthUserMock } from '~/tests/mocks/auth-mock' describe('受保护路由集成测试', () => { const { resetAuthMockUser, setAuthMockUser } = useAuthUserMock() beforeEach(() => { resetAuthMockUser() }) it('未登录状态访问返回预期错误码', async () => { const response = await fetch('/api/your-protected-route') expect(response.status).toBe(401) // 其他响应校验逻辑 }) it('登录状态访问正常返回结果、数据库更新符合预期', async () => { // 先设置登录态 setAuthMockUser('test@company.com', '测试账号') const response = await fetch('/api/your-protected-route', { method: 'POST', body: JSON.stringify({ test: 'data' }) }) expect(response.status).toBe(200) // 后续校验响应内容、数据库字段逻辑 }) })
内容的提问来源于stack exchange,提问作者Lyokolux
相关产品推荐
相关产品推荐

