---
title: "测试工具"
description: "如何测试你的 Nuxt 应用。"
canonical_url: "https://nuxt.zhcndoc.com/docs/4.x/getting-started/testing"
---
# 测试工具

> 如何测试你的 Nuxt 应用。

<tip>

如果你是模块作者，可以在[模块作者指南](https://nuxt.zhcndoc.com/docs/4.x/guide/modules/testing)中找到更具体的信息。

</tip>

<video-accordion title="观看 Alexander Lichter 关于使用 @nuxt/test-utils 入门的视频" video-id="yGzwk9xi9gU">



</video-accordion>

## 安装

为了让你管理其他测试依赖，`@nuxt/test-utils` 附带了各种可选的 peer 依赖。例如：

- 你可以在运行时 Nuxt 环境中选择 `happy-dom` 或 `jsdom`
- 你可以为端到端测试运行器选择 `vitest`、`cucumber`、`jest` 或 `playwright`
- 仅当你希望使用内置的浏览器测试工具（且不使用 `@playwright/test` 作为测试运行器）时，才需要 `playwright-core`

<code-group sync="pm">

```bash [npm]
npm i --save-dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
```

```bash [yarn]
yarn add --dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
```

```bash [pnpm]
pnpm add -D @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
```

```bash [bun]
bun add --dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
```

:: 百汇

## 单元测试

我们目前为需要 Nuxt 运行时环境的代码提供了一个单元测试环境。当前「仅支持 `vitest`」（欢迎贡献以添加其他运行时）。

### 设置

1. （可选）在你的 `nuxt.config` 文件中添加 `@nuxt/test-utils/module`。它会向 Nuxt DevTools 添加一个 Vitest 集成，支持在开发时运行你的单元测试。```tstwoslash
export default defineNuxtConfig({
  modules: [
    '@nuxt/test-utils/module',
  ],
})
```
2. 创建一个包含以下内容的 `vitest.config.ts`：```tstwoslash
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: 'e2e',
          include: ['test/e2e/*.{test,spec}.ts'],
          environment: 'node',
        },
      },
      await defineVitestProject({
        test: {
          name: 'nuxt',
          include: ['test/nuxt/*.{test,spec}.ts'],
          environment: 'nuxt',
        },
      }),
    ],
  },
})
```

<note>

`defineVitestProject` 仅适用于 Nuxt 环境测试。端到端测试应配置为常规的 `test.environment: 'node'` 项目。

</note>
</code-group>

1. 如果你的 Nuxt 环境测试位于 `test/nuxt/` 之外，请参阅[测试中的 TypeScript 支持](#typescript-support-in-tests)，将其添加到 TypeScript 上下文中。

<tip>

在你的 vitest 配置中导入 `@nuxt/test-utils` 时，需要在你的 `package.json` 中指定 `"type": "module"`，或者适当地重命名你的 vitest 配置文件。

> 即，`vitest.config.m{ts,js}`。

</tip>

<tip>

可以使用 `.env.test` 文件为测试设置环境变量。

</tip>

### 使用 Nuxt 运行时环境

使用 [Vitest 项目](https://vitest.dev/guide/projects.html#test-projects)，你可以精确控制哪些测试在何种环境中运行：

- **单元测试**：将常规单元测试放在 `test/unit/` —— 这些在 Node 环境中运行以提高速度
- **Nuxt 测试**：将依赖 Nuxt 运行时环境的测试放在 `test/nuxt/` —— 这些将在 Nuxt 运行时环境中运行

#### 可选：简单设置

如果你更喜欢更简单的设置并希望所有测试都在 Nuxt 环境中运行，可以使用基础配置：

```tstwoslash
import { defineVitestConfig } from '@nuxt/test-utils/config'
import { fileURLToPath } from 'node:url'

export default defineVitestConfig({
  test: {
    environment: 'nuxt',
    // 你可以可选地设置 Nuxt 特定的环境选项
    // environmentOptions: {
    //   nuxt: {
    //     rootDir: fileURLToPath(new URL('./playground', import.meta.url)),
    //     domEnvironment: 'happy-dom', // 'happy-dom'（默认）或 'jsdom'
    //     overrides: {
    //       // 你想传入的其他 Nuxt 配置
    //     }
    //   }
    // }
  },
})
```

如果你使用默认的 `environment: 'nuxt'` 的简单设置，你可以根据需要在每个测试文件中通过特殊注释选择退出 [Nuxt 环境](https://vitest.dev/guide/environment.html#test-environment)。

```tstwoslash
// @vitest-environment node
import { test } from 'vitest'

test('my test', () => {
  // ... 在没有 Nuxt 环境的情况下测试！
})
```

<warning>

不建议使用这种方法，因为它会创建一个混合环境，其中 Nuxt 的 Vite 插件会运行，但 Nuxt 入口和 `nuxtApp` 可能没有被初始化。这可能导致难以调试的错误。

</warning>

### 组织你的测试

使用基于项目的设置，你可能会如下组织你的测试：

```bash [Directory structure]
test/
├── e2e/
│   └── ssr.test.ts
├── nuxt/
│   ├── components.test.ts
│   └── composables.test.ts
├── unit/
│   └── utils.test.ts
```

当然你可以选择任意测试结构，但将 Nuxt 运行时环境与 Nuxt 端到端测试分开对测试稳定性很重要。

#### 测试中的 TypeScript 支持

默认情况下，`test/nuxt/` 和 `tests/nuxt/` 目录中的测试文件会被包含在 [Nuxt 应用的 TypeScript 上下文](https://nuxt.zhcndoc.com/docs/4.x/guide/concepts/typescript#how-nuxt-uses-project-references)中。这意味着它们能够识别 Nuxt 别名（例如 `~/`、`@/`、`#imports`），并且 TypeScript 能识别在 Nuxt 应用中生效的自动导入。

<tip>

这符合推荐的结构，只有需要 Nuxt 运行时环境的测试才放在这些目录中。其他目录（如 `test/unit/`）中的单元测试可以根据需要手动添加。

</tip>

##### 添加其他测试目录

如果您在 Nuxt Vitest 环境中运行的其他目录中有测试，您可以通过将它们添加到配置中，将它们包含在 Nuxt 应用程序 TypeScript 上下文中：

```ts [nuxt.config.ts]
export default defineNuxtConfig({
  typescript: {
    tsConfig: {
      include: [
        // 该路径相对于生成的 .nuxt/tsconfig.json
        '../test/other-nuxt-context/**/*',
      ],
    },
  },
})
```

<important>

单元测试不应依赖于 Nuxt 运行时功能，如自动导入或组合函数。只有当你的测试从源文件（例如 `~/utils/helpers`）导入时，才添加 TypeScript 路径别名支持，而不是针对 Nuxt 特定功能。

</important>

#### 运行测试

使用项目设置，你可以运行不同的测试套件：

```bash
# 运行所有测试
npx vitest

# 仅运行单元测试
npx vitest --project unit

# 仅运行 Nuxt 测试
npx vitest --project nuxt

# 以监听模式运行测试
npx vitest --watch
```

<warning>

当你在 Nuxt 环境中运行测试时，它们将运行在 [`happy-dom`](https://github.com/capricorn86/happy-dom) 或 [`jsdom`](https://github.com/jsdom/jsdom) 环境中。在测试运行之前，一个全局的 Nuxt 应用将被初始化（例如，会运行你在 `app.vue` 中定义的任何插件或代码）。

这意味着你在测试中应该特别注意不要去改变全局状态（或者如果需要改变，测试后请务必重置它）。

</warning>

### 🎭 内置模拟

`@nuxt/test-utils` 为 DOM 环境提供了一些内置模拟。

#### `intersectionObserver`

默认 `true`，创建一个不具功能性的 IntersectionObserver API 的占位类

#### `indexedDB`

默认 `false`，使用 [`fake-indexeddb`](https://github.com/dumbmatter/fakeIndexedDB) 创建一个功能性的 IndexedDB API 模拟

这些可以在你的 `vitest.config.ts` 文件的 `environmentOptions` 部分进行配置：

```tstwoslash
import { defineVitestConfig } from '@nuxt/test-utils/config'

export default defineVitestConfig({
  test: {
    environmentOptions: {
      nuxt: {
        mock: {
          intersectionObserver: true,
          indexedDb: true,
        },
      },
    },
  },
})
```

### 🛠️ 帮助函数

`@nuxt/test-utils` 提供了许多帮助函数以便更方便地测试 Nuxt 应用。

#### `mountSuspended`

`mountSuspended` 允许你在 Nuxt 环境中挂载任意 Vue 组件，支持异步设置并能够访问来自 Nuxt 插件的注入。

<note>

在内部，`mountSuspended` 封装了来自 `@vue/test-utils` 的 `mount`，因此你可以查看 [Vue Test Utils 文档](https://test-utils.vuejs.org/guide/) 以了解可传入选项以及如何使用此工具。

</note>

例如：

```ts [tests/components/SomeComponents.nuxt.spec.ts]twoslash
// @noErrors
import type { Component } from 'vue'

declare module '#components' {
  export const SomeComponent: Component
}
// ---cut---
import { expect, it } from 'vitest'
import { mountSuspended } from '@nuxt/test-utils/runtime'
import { SomeComponent } from '#components'

it('可以挂载某个组件', async () => {
  const component = await mountSuspended(SomeComponent)
  expect(component.text()).toMatchInlineSnapshot(
    '"这是一个自动导入的组件"',
  )
})
```

```ts [tests/App.nuxt.spec.ts]twoslash
import { expect, it } from 'vitest'
import { mountSuspended } from '@nuxt/test-utils/runtime'
import { SomeComponent } from '#components'
import App from '~/app.vue'

it('can also mount an app', async () => {
  const component = await mountSuspended(App, { route: '/test' })
  expect(component.html()).toMatchInlineSnapshot(`
      "<div>这是一个自动导入的组件</div>
      <div> 我是一个全局组件 </div>
      <div>/</div>
      <a href="/test"> 测试链接 </a>"
    `)
})
```

如果你需要监视组件设置状态，可以将 `spy` 选项设为 `true`，并通过 `setupState` 访问 setup 的返回值。

```ts [tests/components/SomeComponents.nuxt.spec.ts]twoslash
// @noErrors
import type { Component } from 'vue'

declare module '#components' {
  export const SomeComponent: Component
}
// ---cut---
import { expect, it, vi } from 'vitest'
import { mountSuspended } from '@nuxt/test-utils/runtime'
import { SomeComponent } from '#components'

it('can spy on setup state', async () => {
  const component = await mountSuspended(SomeComponent, { spy: true })
  vi.mocked(component.setupState.someMethod).mockImplementation(() => 'mocked')
})
```

选项对象接受 `@vue/test-utils` 的挂载选项以及以下属性：

- `route`：初始路由，或设为 `false` 以跳过初始路由更改（默认值为 `/`）。
- `spy`：启用对组件 setup 状态的监视（默认值为 `false`）。

返回对象包含 `@vue/test-utils` 的挂载结果以及以下属性：

- `setupState`：组件 setup 的返回值。

#### `renderSuspended`

`renderSuspended` 允许你在 Nuxt 环境中使用 `@testing-library/vue` 来渲染任意 Vue 组件，支持异步设置并能访问来自 Nuxt 插件的注入。

该方法应与 Testing Library 的实用工具（例如 `screen` 和 `fireEvent`）一起使用。请在你的项目中安装 [@testing-library/vue](https://testing-library.com/docs/vue-testing-library/intro/) 以使用这些功能。

此外，Testing Library 还依赖测试全局变量来进行清理。你应在你的 [Vitest 配置](https://vitest.dev/config/globals) 中启用这些全局变量。

传入的组件将在一个 `<div id="test-wrapper"></div>` 内渲染。

示例：

```ts [tests/components/SomeComponents.nuxt.spec.ts]twoslash
// @noErrors
import type { Component } from 'vue'

declare module '#components' {
  export const SomeComponent: Component
}
// ---cut---
import { expect, it } from 'vitest'
import { renderSuspended } from '@nuxt/test-utils/runtime'
import { SomeComponent } from '#components'
import { screen } from '@testing-library/vue'

it('可以渲染某个组件', async () => {
  await renderSuspended(SomeComponent)
  expect(screen.getByText('这是一个自动导入的组件')).toBeDefined()
})
```

```ts [tests/App.nuxt.spec.ts]twoslash
import { expect, it } from 'vitest'
import { renderSuspended } from '@nuxt/test-utils/runtime'
import App from '~/app.vue'

it('也可以渲染一个应用', async () => {
  const html = await renderSuspended(App, { route: '/test' })
  expect(html).toMatchInlineSnapshot(`
    "<div id="test-wrapper">
      <div>这是一个自动导入的组件</div>
      <div> 我是一个全局组件 </div>
      <div>首页</div><a href="/test"> 测试链接 </a>
    </div>"
  `)
})
```

选项对象接受 `@testing-library/vue` 的 render 选项以及以下属性：

- `route`：初始路由，或设为 `false` 以跳过初始路由更改（默认值为 `/`）。
- `spy`：启用对组件 setup 状态的监视（默认值为 `false`）。请参阅上方的 [`mountSuspended`](#mountsuspended) 示例。

返回对象包含 `@testing-library/vue` 的 render 结果以及以下属性：

- `setupState`：组件 setup 的返回值。

#### `mockNuxtImport`

`mockNuxtImport` 允许你模拟 Nuxt 的自动导入功能。例如，要模拟 `useState`，你可以这样做：

```tstwoslash
import { mockNuxtImport } from '@nuxt/test-utils/runtime'

mockNuxtImport('useState', () => {
  return () => {
    return { value: 'mocked storage' }
  }
})

// 你的测试写在这里
```

你可以显式为 mock 进行类型标注以确保类型安全，并在模拟复杂功能时使用传递给工厂函数的原始实现。

```ts [test/nuxt/import.test.ts]twoslash
import { mockNuxtImport } from '@nuxt/test-utils/runtime'

mockNuxtImport<typeof useState>('useState', (original) => {
  return (...args) => {
    return { ...original('some-key'), value: 'mocked state' }
  }
})

// 或者指定要模拟的目标
mockNuxtImport(useState, (original) => {
  return (...args) => {
    return { ...original('some-key'), value: 'mocked state' }
  }
})

// 你的测试写在这里
```

<note>

`mockNuxtImport` 在每个测试文件中每个被模拟的导入只能使用一次。它实际上是一个宏，会被转换为 `vi.mock`，而 `vi.mock` 会被提升，详见 [Vitest 文档](https://vitest.dev/api/vi#vi-mock)。

</note>

如果你需要在不同测试之间为 Nuxt 导入提供不同的实现，可以通过创建并暴露你的模拟（使用 [`vi.hoisted`](https://vitest.dev/api/vi#vi-hoisted)）来实现，然后在 `mockNuxtImport` 中使用这些模拟。这样你可以访问被模拟的导入，并在测试之间更改实现。注意在每个测试前后恢复模拟状态以撤销模拟的状态更改（参见 [restore mocks](https://vitest.dev/api/mock#mockrestore)）。

```tstwoslash
import { vi } from 'vitest'
import { mockNuxtImport } from '@nuxt/test-utils/runtime'

const { useStateMock } = vi.hoisted(() => {
  return {
    useStateMock: vi.fn(() => {
      return { value: 'mocked storage' }
    }),
  }
})

mockNuxtImport('useState', () => {
  return useStateMock
})

// 然后，在测试内部
useStateMock.mockImplementation(() => {
  return { value: '其他内容' }
})
```

如果你需要仅在测试内部模拟行为，也可以使用以下方法。

```tstwoslash
import { beforeEach, vi } from 'vitest'
import { mockNuxtImport } from '@nuxt/test-utils/runtime'

mockNuxtImport(useRoute, original => vi.fn(original))

beforeEach(() => {
  vi.resetAllMocks()
})

// 然后，在测试内部
const useRouteOriginal = vi.mocked(useRoute).getMockImplementation()!
vi.mocked(useRoute).mockImplementation(
  (...args) => ({ ...useRouteOriginal(...args), path: '/mocked' }),
)
```

#### `mockComponent`

`mockComponent` 允许你模拟 Nuxt 的组件。<br />


第一个参数可以是 PascalCase 的组件名，或组件的相对路径。<br />


第二个参数是返回被模拟组件的工厂函数。

例如，要模拟 `MyComponent`，你可以：

```tstwoslash
import { mockComponent } from '@nuxt/test-utils/runtime'

mockComponent('MyComponent', {
  props: {
    value: String,
  },
  setup (props) {
    // ...
  },
})

// 相对路径或别名也可
mockComponent('~/components/my-component.vue', () => {
  // 或者一个工厂函数
  return defineComponent({
    setup (props) {
      // ...
    },
  })
})

// 或者你可以使用 SFC 将其重定向到一个模拟组件
mockComponent('MyComponent', () => import('./MockComponent.vue'))

// 你的测试写在这里
```

> 注意：工厂函数由于会被提升，不能在其中引用本地变量。如果你需要访问 Vue API 或其他变量，需要在工厂函数中导入它们。

```tstwoslash
import { mockComponent } from '@nuxt/test-utils/runtime'

mockComponent('MyComponent', async () => {
  const { ref, h } = await import('vue')

  return defineComponent({
    setup (props) {
      const counter = ref(0)
      return () => h('div', null, counter.value)
    },
  })
})
```

#### `registerEndpoint`

`registerEndpoint` 允许你创建返回模拟数据的 Nitro 接口。当你想测试一个向 API 发起请求以显示数据的组件时，这非常有用。

第一个参数是接口名称（例如 `/test/`）。<br />


第二个参数是一个返回模拟数据的工厂函数。

例如，要模拟 `/test/` 接口，你可以：

```tstwoslash
import { registerEndpoint } from '@nuxt/test-utils/runtime'

registerEndpoint('/test/', () => ({
  test: 'test-field',
}))
```

默认情况下，请求将使用 `GET` 方法。你可以通过将第二个参数设置为对象（而不是函数）来使用其他方法。

```tstwoslash
import { registerEndpoint } from '@nuxt/test-utils/runtime'

registerEndpoint('/test/', {
  method: 'POST',
  handler: () => ({ test: 'test-field' }),
})
```

此对象接受以下属性：

- `handler`：事件处理函数
- `method`：（可选）匹配的 HTTP 方法（例如 'GET'、'POST'）
- `once`：（可选）如果为 true，处理程序只会用于第一个匹配的请求，然后自动移除

> **注意**：如果你的组件中的请求指向外部 API，你可以使用 `baseURL`，然后使用 [Nuxt 环境覆盖配置](https://nuxt.zhcndoc.com/docs/4.x/getting-started/configuration#environment-overrides)（`$test`）将其置空，这样你的所有请求都会指向 Nitro 服务器。

- `handler`：事件处理函数
- `method`：（可选）匹配的 HTTP 方法（例如 `'GET'`、`'POST'`）
- `once`：（可选）如果为 true，处理程序只会用于第一个匹配的请求，然后自动移除

> **注意**：如果你的组件中的请求指向外部 API，你可以使用 `baseURL`，然后使用 [Nuxt 环境覆盖配置](https://nuxt.zhcndoc.com/docs/4.x/getting-started/configuration#environment-overrides)（`$test`）将其置空，这样你的所有请求都会指向 Nitro 服务器。

#### 与端到端测试的冲突

`@nuxt/test-utils/runtime` 和 `@nuxt/test-utils/e2e` 需要在不同的测试环境中运行，因此不能在同一个文件中同时使用。

如果你想同时使用 `@nuxt/test-utils` 的端到端和单元测试功能，可以将测试拆分到不同的文件中。然后你可以为每个文件用特殊注释指定测试环境 `// @vitest-environment nuxt`，或者将运行时单元测试文件命名为 `.nuxt.spec.ts` 扩展名。

`app.nuxt.spec.ts`

```tstwoslash
import { mockNuxtImport } from '@nuxt/test-utils/runtime'

mockNuxtImport('useState', () => {
  return () => {
    return { value: 'mocked storage' }
  }
})
```

`app.e2e.spec.ts`

```tstwoslash
import { $fetch, setup } from '@nuxt/test-utils/e2e'

await setup({
  setupTimeout: 10000,
})

// ...
```

### 使用 `@vue/test-utils`

如果你更愿意单独使用 `@vue/test-utils` 在 Nuxt 中进行单元测试，且你仅测试不依赖 Nuxt composables、自动导入或上下文的组件，可以按以下步骤设置。

1. 安装所需依赖<code-group sync="pm">

```bash [npm]
npm i --save-dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue
```

```bash [yarn]
yarn add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue
```

```bash [pnpm]
pnpm add -D vitest @vue/test-utils happy-dom @vitejs/plugin-vue
```

```bash [bun]
bun add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue
```

</code-group>
2. 创建一个包含以下内容的 `vitest.config.ts`：```tstwoslash
import { defineConfig } from 'vitest/config'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  test: {
    environment: 'happy-dom',
  },
})
```
3. 在你的 `package.json` 中添加一个测试脚本```json
"scripts": {
  "build": "nuxt build",
  "dev": "nuxt dev",
  ...
  "test": "vitest"
},
```
4. 创建一个简单的 `<HelloWorld>` 组件 `app/components/HelloWorld.vue`，内容如下：```vue
<template>
  <p>你好，世界</p>
</template>
```
5. 为这个新建组件创建一个简单的单元测试 `~/components/HelloWorld.spec.ts````tstwoslash
import { describe, expect, it } from 'vitest'
import { mount } from '@vue/test-utils'

import HelloWorld from './HelloWorld.vue'

describe('HelloWorld', () => {
  it('组件正确渲染你好，世界', () => {
    const wrapper = mount(HelloWorld)
    expect(wrapper.text()).toContain('你好，世界')
  })
})
```
6. 运行 vitest 命令<code-group sync="pm">

```bash [npm]
npm run test
```

```bash [yarn]
yarn test
```

```bash [pnpm]
pnpm run test
```

```bash [bun]
bun run test
```

</code-group>

恭喜，你现在已准备好在 Nuxt 中使用 `@vue/test-utils` 进行单元测试！祝测试愉快！

### Vitest 浏览器模式

`@nuxt/test-utils` 通过 `@nuxt/test-utils/browser` 提供了在 [Vitest 浏览器模式](https://vitest.dev/guide/browser/)中进行测试的帮助函数。

#### 设置

1. 安装 Vitest 浏览器包和浏览器提供程序：<code-group sync="pm">

```bash [npm]
npm i --save-dev @vitest/browser-playwright
```

```bash [yarn]
yarn add --dev @vitest/browser-playwright
```

```bash [pnpm]
pnpm add -D @vitest/browser-playwright
```

```bash [bun]
bun add --dev @vitest/browser-playwright
```

</code-group>
2. 在你的 `vitest.config.ts` 中配置浏览器模式：```tstwoslash
// @errors: 2307
// ---cut---
import { defineConfig } from 'vitest/config'
import { defineVitestProject } from '@nuxt/test-utils/config'
import { playwright } from '@vitest/browser-playwright'

export default defineConfig({
  test: {
    projects: [
      await defineVitestProject({
        test: {
          name: 'browser',
          include: ['test/browser/**/*.{test,spec}.ts'],
          browser: {
            enabled: true,
            provider: playwright(),
            instances: [{ browser: 'chromium' }],
          },
          // If you want to enable `page.render` and automatic cleanup, add this setup file
          setupFiles: ['@nuxt/test-utils/browser'],
        },
      }),
    ],
  },
})
```

<note>

将 `@nuxt/test-utils/browser` 添加到 `setupFiles` 时，如果 TypeScript 没有自动识别 `page.render` 的类型，你可以通过 `nuxt.config.ts` 中的 `typescript.tsConfig.compilerOptions.types` 添加 `@nuxt/test-utils/browser`。

</note>

<note>

如果你的 Vitest 浏览器模式测试位于 `test/nuxt/` 之外（例如 `test/browser/`），请参阅[添加其他测试目录](#adding-other-test-directories)，将它们添加到 TypeScript 上下文中。

</note>

#### 使用

你可以使用 `vitest/browser` 中的 `page.render`：

```ts [test/browser/components/MyCounter.nuxt.spec.ts]twoslash
// @noErrors
import type { Component } from 'vue'

declare module '#components' {
  export const MyCounter: Component
}
// ---cut---
import { expect, it } from 'vitest'
import { page } from 'vitest/browser'
// If you added the setup file, the following import is not needed
import '@nuxt/test-utils/browser'

import { MyCounter } from '#components'

it('counter button increments the count', async () => {
  const screen = await page.render(MyCounter)
  await screen.getByRole('button', { name: 'Increment' }).click()
  await expect.element(screen.getByText('Count: 1')).toBeVisible()
})
```

你也可以直接从 `@nuxt/test-utils/browser` 导入 `render`：

```tstwoslash
// @noErrors
import type { Component } from 'vue'

declare module '#components' {
  export const MyCounter: Component
}
// ---cut---
import { expect, it } from 'vitest'
import { render } from '@nuxt/test-utils/browser'
import { MyCounter } from '#components'

it('can render using the render helper', async () => {
  const screen = await render(MyCounter)
  await screen.getByRole('button', { name: 'Increment' }).click()
  await expect.element(screen.getByText('Count: 1')).toBeVisible()
})
```

选项对象接受 `@vue/test-utils` 的挂载选项（使用 `container` 而不是 `attachTo`）以及以下属性：

- `route`：初始路由，或设为 `false` 以跳过初始路由更改（默认值为 `/`）。
- `spy`：启用对组件 setup 状态的监视（默认值为 `false`）。请参阅上方的 [`mountSuspended`](#mountsuspended) 示例。
- `container`：用于渲染的自定义 `HTMLElement` 容器（使用此选项代替 `@vue/test-utils` 的 `attachTo`）。
- `baseElement`：自定义基础 `HTMLElement`（默认值为 `document.body`）。

返回对象包含以下属性：

- `container`：组件渲染到其中的容器 `HTMLElement`。
- `baseElement`：基础 `HTMLElement`（默认值为 `document.body`）。
- `locator`：根元素 `Locator`。
- `setupState`：组件 setup 的返回值；启用 `spy` 选项时为模拟值。
- `debug()`：将格式化后的 DOM 打印到控制台。
- `unmount()`：卸载组件。也会记录一个 `nuxt.unmount` 跟踪标记。
- `emitted()`：获取触发的事件。
- `rerender(props)`：使用新 props 重新渲染组件。也会记录一个 `nuxt.rerender` 跟踪标记。

## 端到端测试

对于端到端测试，我们支持 [Vitest](https://github.com/vitest-dev/vitest)、[Jest](https://jestjs.io)、[Cucumber](https://cucumber.io/) 和 [Playwright](https://playwright.dev/) 作为测试运行器。

### 设置

在每个使用 `@nuxt/test-utils/e2e` 辅助方法的 `describe` 块中，你需要在开始之前设置测试上下文。

```ts [test/my-test.spec.ts]twoslash
import { describe, test } from 'vitest'
import { $fetch, setup } from '@nuxt/test-utils/e2e'

describe('我的测试', async () => {
  await setup({
    // 测试上下文选项
  })

  test('我的测试', () => {
    // ...
  })
})
```

在背后，`setup` 会在 `beforeAll`、`beforeEach`、`afterEach` 和 `afterAll` 中执行一系列任务，以正确设置 Nuxt 测试环境。

请使用下面列出的 `setup` 方法选项。

#### Nuxt 配置

- `rootDir`：要进行测试的 Nuxt 应用目录路径。

  - 类型：`string`
  - 默认：`'.'`
- `configFile`：配置文件的名称。

  - 类型：`string`
  - 默认：`'nuxt.config'`

#### 时间设置

- `setupTimeout`：允许运行 `setupTest` 完成工作的时间（毫秒），这可能包括为 Nuxt 应用构建或生成文件，具体取决于传入的选项。
  - 类型：`number`
  - 默认：`120000`（Windows 上为 `240000`）
- `teardownTimeout`：允许拆除测试环境（如关闭浏览器）所需的时间（毫秒）。
  - 类型：`number`
  - 默认：`30000`

#### 功能选项

- `build`：是否运行单独的构建步骤。
  - 类型：`boolean`
  - 默认：`true`（当 `browser` 或 `server` 被禁用，或提供了 `host` 时为 `false`）
- `server`：是否启动一个服务器以响应测试套件中的请求。
  - 类型：`boolean`
  - 默认：`true`（当提供 `host` 时为 `false`）
- `port`：如果提供，将把启动的测试服务器端口设置为该值。
  - 类型：`number | undefined`
  - 默认：`undefined`
- `host`：如果提供，则使用该 URL 作为测试目标，而不是构建并运行新服务器。这适用于针对已部署版本的应用，或针对已运行的本地服务器（这可能显著缩短测试执行时间）运行「真实」的端到端测试。请参阅下方的[目标主机端到端示例](https://nuxt.zhcndoc.com/docs/4.x/getting-started/testing#target-host-end-to-end-example)。
  - 类型：`string`
  - 默认：`undefined`
- `browser`：在底层，Nuxt 测试工具使用 [`playwright`](https://playwright.dev) 进行浏览器测试。如果设置此选项，将会启动一个浏览器，并可在随后测试套件中进行控制。
  - 类型：`boolean`
  - 默认：`false`
- `browserOptions`
  - 类型：包含以下属性的 `object`
    - `type`：要启动的浏览器类型 —— `chromium`、`firefox` 或 `webkit`
    - `launch`：在启动浏览器时将传递给 playwright 的选项对象。参见[完整 API 参考](https://playwright.dev/docs/api/class-browsertype#browser-type-launch)。
- `runner`：为测试套件指定运行器。目前建议使用 [Vitest](https://vitest.dev)。
  - 类型：`'vitest' | 'jest' | 'cucumber'`
  - 默认：`'vitest'`
- `logLevel`：覆盖服务器子进程的 consola 日志级别。（使用 NUXT_TEST_LOG_LEVEL 环境变量进行覆盖）
  - 类型：`number`
  - 默认：`1`
- `captureServerLogs`：是否捕获服务器进程输出，而不是继承 stdio。当设置为 `true`（默认值）时，服务器 stdout/stderr 不会输出到控制台，而是可通过 `getServerLogs()` 访问。设置为 `false` 可恢复旧的继承 stdio 行为（适合在本地调试测试）。
  - 类型：`boolean`
  - 默认：`true`

##### 目标 `host` 端到端示例

端到端测试的常见用例是在与你通常用于生产的环境相同的环境中对已部署的应用运行测试。

在本地开发或自动部署流水线中，对一个单独的本地服务器进行测试通常更加高效，并且通常比在测试间让测试框架重新构建要快很多。

要为端到端测试使用单独的目标主机，只需在 `setup` 函数中提供所需 URL 的 `host` 属性。

```ts
import { createPage, setup } from '@nuxt/test-utils/e2e'
import { describe, expect, it } from 'vitest'

describe('登录页面', async () => {
  await setup({
    host: 'http://localhost:8787',
  })

  it('显示电子邮件和密码字段', async () => {
    const page = await createPage('/login')
    expect(await page.getByTestId('email').isVisible()).toBe(true)
    expect(await page.getByTestId('password').isVisible()).toBe(true)
  })
})
```

### API

#### `$fetch(url)`

获取服务端渲染页面的 HTML。

```tstwoslash
import { $fetch } from '@nuxt/test-utils/e2e'

const html = await $fetch('/')
```

#### `fetch(url)`

获取服务端渲染页面的响应。

```tstwoslash
import { fetch } from '@nuxt/test-utils/e2e'

const res = await fetch('/')
const { body, headers } = res
```

#### `url(path)`

获取给定页面的完整 URL（包括测试服务器运行的端口）。

```tstwoslash
import { url } from '@nuxt/test-utils/e2e'

const pageUrl = url('/page')
// 'http://localhost:6840/page'
```

#### `getServerLogs()`

返回自上次调用 `startServer()`（或 `clearServerLogs()`）以来从服务器子进程 stdout/stderr 中捕获的日志行。仅当 `captureServerLogs` 为 `true`（默认值）时才会填充。

```tstwoslash
import { expect, it, vi } from 'vitest'
import { $fetch, clearServerLogs, getServerLogs } from '@nuxt/test-utils/e2e'

it('captures console.log output from a server route', async () => {
  clearServerLogs()
  await $fetch('/api/log-test')
  await vi.waitFor(() => {
    expect(getServerLogs().some(line => line.includes('[test] server-log-marker'))).toBe(true)
  })
})
```

#### `clearServerLogs()`

清除已捕获的服务器日志行。当你希望仅断言特定操作产生的日志时，可在请求之间使用此方法。

### 在浏览器中测试

我们在 `@nuxt/test-utils` 中为 Playwright 提供了内置支持，可以以编程方式或通过 Playwright 测试运行器使用。

#### `createPage(url)`

在 `vitest`、`jest` 或 `cucumber` 中，你可以使用 `createPage` 创建一个经过配置的 Playwright 浏览器实例，并且可以选择让它访问运行中服务器上的某个路径。你可以在 [Playwright 文档](https://playwright.dev/docs/api/class-page) 中详细了解可用的 API 方法。

```tstwoslash
import { createPage } from '@nuxt/test-utils/e2e'

const page = await createPage('/page')
// 你可以通过 `page` 变量访问所有 Playwright API
```

#### 使用 Playwright 测试运行器进行测试

我们也为在 [Playwright 测试运行器](https://playwright.dev/docs/intro) 中测试 Nuxt 提供了高级支持。

<code-group sync="pm">

```bash [npm]
npm i --save-dev @playwright/test @nuxt/test-utils
```

```bash [yarn]
yarn add --dev @playwright/test @nuxt/test-utils
```

```bash [pnpm]
pnpm add -D @playwright/test @nuxt/test-utils
```

```bash [bun]
bun add --dev @playwright/test @nuxt/test-utils
```

```bash [deno]
deno add --dev npm:@playwright/test npm:@nuxt/test-utils
```

</code-group>

你可以提供全局 Nuxt 配置，格式与前面提到的 `setup()` 函数相同。

```ts [playwright.config.ts]
import { fileURLToPath } from 'node:url'
import { defineConfig, devices } from '@playwright/test'
import type { ConfigOptions } from '@nuxt/test-utils/playwright'

export default defineConfig<ConfigOptions>({
  use: {
    nuxt: {
      rootDir: fileURLToPath(new URL('.', import.meta.url)),
    },
  },
  // ...
})
```

<read-more target="_blank" title="查看完整示例配置" to="https://github.com/nuxt/test-utils/blob/main/examples/app-playwright/playwright.config.ts">



</read-more>

然后你的测试文件应该直接从 `@nuxt/test-utils/playwright` 使用 `expect` 和 `test`：

```ts [tests/example.test.ts]
import { expect, test } from '@nuxt/test-utils/playwright'

test('test', async ({ page, goto }) => {
  await goto('/', { waitUntil: 'hydration' })
  await expect(page.getByRole('heading')).toHaveText('欢迎使用 Playwright！')
})
```

你也可以在测试文件内直接配置 Nuxt 服务器：

```ts [tests/example.test.ts]
import { expect, test } from '@nuxt/test-utils/playwright'

test.use({
  nuxt: {
    rootDir: fileURLToPath(new URL('..', import.meta.url)),
  },
})

test('test', async ({ page, goto }) => {
  await goto('/', { waitUntil: 'hydration' })
  await expect(page.getByRole('heading')).toHaveText('欢迎使用 Playwright！')
})
```


## Sitemap

See the full [sitemap](https://nuxt.zhcndoc.com/sitemap.md) for all pages.
