测试:@nuxt/test-utils
本教程共 50 篇 · 第 45 篇 · 更新于 2026-08-08 · 约 9 分钟阅读
本节目标:理解 Nuxt 的官方测试方案,能用
@nuxt/test-utils配合 Vitest 给组件、组合式函数写测试,并了解端到端测试的基本做法。
45-1
功能越写越多,你总担心「改了 A 会不会弄坏 B」。测试就是这道保险:写一段小代码,自动验证某个组件渲染对不对、某个函数返回对不对。Nuxt 官方提供了 @nuxt/test-utils,它在 Nuxt 真实运行环境里跑测试,所以组件自动导入、$fetch、插件注入这些都能用,测出来的结果更可信。
Note测试运行器我们用 Vitest(快、和 Vite 同源)。
@nuxt/test-utils也支持 Playwright、Jest、Cucumber 做端到端,但本教程聚焦最主流的 Vitest 用法。
45-2
npm i --save-dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
happy-dom/jsdom:在 Node 里模拟浏览器环境(二选一)。@vue/test-utils:挂载和操作 Vue 组件。playwright-core:内置浏览器测试能力(可选,端到端才需要)。
45-3
在项目根创建 vitest.config.ts。推荐用「项目(projects)」方式,把普通单元测试和需要 Nuxt 环境的测试分开:
import { defineConfig } from 'vitest/config'
import { defineVitestProject } from '@nuxt/test-utils/config'
export default defineConfig({
test: {
projects: [
{
test: {
name: 'unit',
include: ['test/unit/*.{test,spec}.ts'],
environment: 'node',
},
},
{
test: {
name: 'nuxt',
include: ['test/nuxt/*.{test,spec}.ts'],
environment: 'nuxt',
},
},
],
},
})
defineVitestProject 专门用于「需要 Nuxt 运行时」的测试;纯逻辑单测放 test/unit/,用普通 node 环境跑,更快。
Tip想要所有测试都跑在 Nuxt 环境下、图省事,可以用
defineVitestConfig并直接设environment: 'nuxt'。但按项目分开更稳定,官方也推荐分离。
45-4
组件测试用 mountSuspended,它能在 Nuxt 环境里挂载组件,支持异步 setup、能访问插件注入:
import { mountSuspended } from '@nuxt/test-utils/runtime'
import { expect, it } from 'vitest'
import { SomeComponent } from '#components'
it('能挂载某个组件', async () => {
const component = await mountSuspended(SomeComponent)
expect(component.text()).toMatchInlineSnapshot('"这是自动导入的组件"')
})
#components 是 Nuxt 自动导入的组件别名,测试里也能用。你还可以给 mountSuspended 传 route 选项,指定初始路由:
import { mountSuspended } from '@nuxt/test-utils/runtime'
import { expect, it } from 'vitest'
import App from '~/app.vue'
it('也能挂载整个应用', async () => {
const component = await mountSuspended(App, { route: '/test' })
expect(component.html()).toContain('Test link')
})
Note默认的 DOM 环境是
happy-dom。部分 API(如IntersectionObserver)需要 mock。@nuxt/test-utils内置了对intersectionObserver的空实现,可在environmentOptions.mock里开关。
45-5
组合式函数依赖 Nuxt 上下文,同样要在 Nuxt 环境测试。常用 mockNuxtImport 来模拟某个自动导入:
import { mockNuxtImport } from '@nuxt/test-utils/runtime'
import { expect, it } from 'vitest'
mockNuxtImport('useState', () => {
return () => ({ value: 'mocked storage' })
})
it('模拟了 useState', () => {
// 你的测试
})
还能 mock 组件和 Nitro 接口:
import { registerEndpoint } from '@nuxt/test-utils/runtime'
registerEndpoint('/test/', () => ({ test: 'test-field' }))
这样组件里请求 /test/ 时,拿到的是 mock 数据,不必真起后端。
Warning
mockNuxtImport在单个测试文件里对每个被 mock 的导入只能用一次(它会被提升成vi.mock)。需要在多个测试间切换实现时,用vi.hoisted创建可变的 mock,并在beforeEach里vi.resetAllMocks()清理。
45-6
npx vitest # 跑全部
npx vitest --project unit # 只跑普通单测
npx vitest --project nuxt # 只跑 Nuxt 环境测试
npx vitest --watch # 监听模式
Tip在
nuxt.config里加@nuxt/test-utils/module,能把测试集成进 Nuxt DevTools,开发时直接在面板里跑单测。
45-7
端到端测试会真正启动一个 Nuxt 服务,用 Playwright 控制浏览器点击页面。核心是先 setup:
import { $fetch, setup } from '@nuxt/test-utils/e2e'
import { describe, expect, it } from 'vitest'
describe('我的测试', async () => {
await setup({})
it('首页能返回 HTML', async () => {
const html = await $fetch('/')
expect(html).toContain('__nuxt')
})
})
$fetch 取服务端渲染页面的 HTML,createPage 能开一个真实浏览器页面做交互断言。端到端测试更慢,建议和单元测试分目录(test/e2e/),且 @nuxt/test-utils/runtime 与 /e2e 不能在同一个文件混用。
WarningNuxt 环境下的测试会先初始化一个全局应用(包括跑
app.vue、插件)。测试里不要乱改全局状态;要改也得在测试后还原,否则一个测试污染另一个。
45-8
Nuxt 3 与 Nuxt 4 的测试方案一致,@nuxt/test-utils + Vitest 跨版本通用。测试里引用的 ~/app.vue、#components 等别名,会跟随你项目的目录约定(Nuxt 4 下指向 app/)。
45-9
一个能直接抄的组件测试长这样:
import { mountSuspended } from '@nuxt/test-utils/runtime'
import { expect, it } from 'vitest'
import MyButton from '~/components/MyButton.vue'
it('按钮显示传入的文字', async () => {
const wrapper = await mountSuspended(MyButton, {
props: { label: '提交' },
})
expect(wrapper.text()).toContain('提交')
})
mountSuspended 跑在 Nuxt 环境里,组件里用到的自动导入、$fetch、插件注入都能用,所以测出来的结果跟真实运行一致。wrapper.text() 取渲染文本,wrapper.find('button').trigger('click') 还能模拟点击后断言状态变化。
45-10
两类测试职责不同,别混:
- 单元测试(含 Nuxt 组件/组合式测试):测「一个零件对不对」,快、隔离、可 mock,应该占测试的大头。
- 端到端(e2e):测「整条链路通不通」,真起服务、真开浏览器点页面,慢但最接近用户。只挑核心流程(登录、下单、关键导航)写几条,别给每个页面都写 e2e。
Warning一个常见误区是把所有逻辑都塞进 e2e。e2e 慢且容易因环境波动变「flaky」(偶发失败)。能单测覆盖的,就别用 e2e 凑数。
测试文件放在哪?约定放在项目根的test/目录,再按unit/、nuxt/、e2e/分子目录,和前面vitest.config.ts里的include对应。组件、组合式测试进test/nuxt/(跑在 Nuxt 环境),纯函数逻辑测试进test/unit/(跑在 node 环境,更快)。别把测试文件混进app/,否则 Nuxt 会把它当成应用源码去扫描、打包,既拖慢构建又可能引入奇怪的循环依赖。
把测试接进 CI 也很简单:在 GitHub Actions 里加一步 npx vitest run,每次推代码自动跑。注意 CI 里要用 vitest run(一次性跑完就退出),不是 vitest(监听模式会一直挂着)。端到端测试在 CI 里需要浏览器环境,@nuxt/test-utils 的 e2e 会自己拉起 Nuxt 服务和 Playwright,通常再加一步装浏览器依赖即可。
Note测试整体跑得慢,多半是 Nuxt 环境测试(每个文件都要初始化全局应用)。把能抽成纯函数的逻辑尽量用
test/unit/覆盖,Nuxt 环境测试只留给「必须用到自动导入、插件」的场景,整体会快很多。
45-11
用 @nuxt/test-utils + Vitest:组件测试靠 mountSuspended,组合式/接口靠 mockNuxtImport/registerEndpoint,端到端靠 setup + $fetch。先分离 unit/nuxt 项目再跑。下一章看怎么把应用部署上线。
45-7 测试策略与测试金字塔
一个健康的测试策略应该遵循测试金字塔原则:大量的单元测试在底层、适量的集成测试在中间、少量的端到端测试在顶层。在 Nuxt 项目里,单元测试覆盖组合式函数和工具函数,集成测试验证组件交互和 API 路由,端到端测试模拟用户完整操作流程。
Vitest 是 Nuxt 生态里最主流的测试框架,它和 Vite 构建工具深度集成,运行速度快,配置简单。配合 @nuxt/test-utils,你可以方便地挂载 Nuxt 组件、模拟路由、测试数据获取逻辑。
编写测试时,优先测试业务逻辑而不是 UI 细节。业务逻辑变化慢,测试稳定;UI 细节经常调整,测试容易失效。一个好的测试应该在代码行为改变时失败,在代码重构但行为不变时通过。
45-8 端到端测试的编写要点
端到端测试(E2E)模拟真实用户在浏览器里的操作,是测试金字塔的顶层。在 Nuxt 项目里,推荐使用 Cypress 或 Playwright 做端到端测试。两者都能启动真实的浏览器,模拟点击、输入、导航等操作。
编写端到端测试时,关注用户的核心操作流程:注册登录、搜索商品、提交订单、修改设置等。每个测试用例应该独立运行,不依赖其他用例的状态。测试数据用固定的测试账号和模拟数据,避免依赖生产环境的真实数据。
端到端测试的运行速度比单元测试慢很多,不适合每次保存文件时都跑。建议把它集成到 CI 流程里,每次提交代码或合并分支时自动运行。如果测试时间过长,可以按功能模块分组并行执行。