首页 / Nuxt 4 入门教程 / 测试:@nuxt/test-utils

Nuxt 4 入门教程

测试:@nuxt/test-utils

本教程共 50 篇 · 第 45 篇 · 更新于 2026-08-08 · 约 9 分钟阅读

NuxtNuxt4测试vitestnuxt-test-utils单元测试e2e

本节目标:理解 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 自动导入的组件别名,测试里也能用。你还可以给 mountSuspendedroute 选项,指定初始路由:

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,并在 beforeEachvi.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 不能在同一个文件混用。

Warning

Nuxt 环境下的测试会先初始化一个全局应用(包括跑 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 流程里,每次提交代码或合并分支时自动运行。如果测试时间过长,可以按功能模块分组并行执行。