测试

如何测试你的 Nuxt 应用。

如果你是模块作者,可以在模块作者指南中找到更具体的信息。

Nuxt 通过 @nuxt/test-utils 提供对端到端和单元测试的一流支持,这是一个测试工具和配置库,目前驱动着 Nuxt 自身使用的测试 以及整个模块生态的测试。

安装

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

  • 你可以在 happy-dom 和 jsdom 之间选择 Nuxt 运行时环境
  • 你可以在 vitest、cucumber、jest 和 playwright 之间选择端到端测试运行器
  • playwright-core 仅在你想使用内置浏览器测试工具(且不使用 @playwright/test 作为测试运行器)时才需要
npm i --save-dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
yarn add --dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
pnpm add -D @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
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 集成,支持在开发中运行单元测试。
export default defineNuxtConfig({
  modules: [
    '@nuxt/test-utils/module',
  ],
})
  1. 创建 vitest.config.ts,内容如下:
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',
        },
      }),
    ],
  },
})

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

3. 如果你的 Nuxt 环境测试位于 `test/nuxt/` 之外,参见[测试中的 TypeScript 支持](#测试中的-typescript-支持)将它们添加到 TypeScript 上下文。

在 vitest 配置中导入 @nuxt/test-utils 时,需要在 package.json 中指定 "type": "module" 或相应重命名 vitest 配置文件。

即 vitest.config.m{ts,js}。

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

使用 Nuxt 运行时环境

使用 Vitest 项目,你可以细粒度控制哪些测试在哪个环境中运行:

  • 单元测试:将普通单元测试放在 test/unit/ - 这些在 Node 环境中运行以加速
  • Nuxt 测试:将依赖 Nuxt 运行时环境的测试放在 test/nuxt/ - 这些将在 Nuxt 运行时环境中运行

替代方案:简单设置

如果你偏好更简单的设置,希望所有测试都在 Nuxt 环境中运行,可以使用基本配置:

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 环境。

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

test('my test', () => {
  // ... 不使用 Nuxt 环境的测试!
})

不推荐这种方式,因为它创建了一个混合环境:Nuxt Vite 插件运行但 Nuxt 入口和 nuxtApp 未初始化。这可能导致难以调试的错误。

组织测试

使用基于项目的设置,你可以如下组织测试:

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 上下文中。这意味着它们会识别 Nuxt 别名(如 ~/、@/、#imports),TypeScript 会感知在你的 Nuxt 应用中工作的自动导入。

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

添加其他测试目录

如果在其他目录中有要在 Nuxt Vitest 环境中运行的测试,可以将它们添加到 Nuxt 应用 TypeScript 上下文中:

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

单元测试不应依赖 Nuxt 运行时特性如自动导入或 composables。仅当测试从你的源文件导入(如 ~/utils/helpers)时才添加 TypeScript 路径别名支持,而不是为了 Nuxt 特定特性。

运行测试

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

# 运行所有测试
npx vitest

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

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

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

在 Nuxt 环境中运行测试时,它们将在 happy-dom 或 jsdom 环境中运行。测试运行前,会初始化一个全局 Nuxt 应用(包括,例如,运行你在 app.vue 中定义的任何插件或代码)。

这意味着你应特别注意不要在测试中修改全局状态(或者如果需要,之后重置它)。

🎭 内置 Mock

@nuxt/test-utils 为 DOM 环境提供一些内置 mock。

intersectionObserver

默认 true,为 IntersectionObserver API 创建一个没有任何功能的虚拟类

indexedDB

默认 false,使用 fake-indexeddb 创建 IndexedDB API 的功能 mock

可以在 vitest.config.ts 文件的 environmentOptions 部分配置:

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 组件,支持异步 setup 和访问 Nuxt 插件的注入。

底层,mountSuspended 封装了 @vue/test-utils 的 mount,所以可以查看 Vue Test Utils 文档 了解更多可传递的选项和使用方法。

例如:

// @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(
    '"This is an auto-imported component"',
  )
})
import { expect, it } from 'vitest'
import { mountSuspended } from '@nuxt/test-utils/runtime'
import App from '~/app.vue'

it('也可以挂载一个应用', async () => {
  const component = await mountSuspended(App, { route: '/test' })
  expect(component.html()).toMatchInlineSnapshot(`
      "<div>This is an auto-imported component</div>
      <div> I am a global component </div>
      <div>/</div>
      <a href="/test"> Test link </a>"
    `)
})

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

// @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('可以监视 setup 状态', 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 组件,支持异步 setup 和访问 Nuxt 插件的注入。

应与 Testing Library 的工具一起使用,例如 screen 和 fireEvent。在项目中安装 @testing-library/vue 以使用这些。

此外,Testing Library 还依赖测试全局变量进行清理。你应该在 Vitest 配置中启用这些。

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

示例:

// @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('This is an auto-imported component')).toBeDefined()
})
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>This is an auto-imported component</div>
      <div> I am a global component </div>
      <div>Index page</div><a href="/test"> Test link </a>
    </div>"
  `)
})

选项对象接受 @testing-library/vue 渲染选项和以下属性:

  • route:初始路由,或 false 跳过初始路由切换(默认 /)。
  • spy:启用监视组件 setup 状态(默认 false)。参见上面的 mountSuspended 示例。

返回对象包含 @testing-library/vue 渲染结果和以下属性:

  • setupState:组件 setup 的返回值。

mockNuxtImport

mockNuxtImport 允许你 mock Nuxt 的自动导入功能。例如,要 mock useState,可以这样做:

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

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

// 你的测试在这里

你可以显式指定 mock 的类型以获得类型安全,并在 mock 复杂功能时使用传给工厂函数的原始实现。

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

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

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

// 你的测试在这里

mockNuxtImport 每个测试文件中每个被 mock 的导入只能使用一次。它实际上是一个宏,会被转换为 vi.mock,而 vi.mock 是提升的,如 Vitest 文档所述。

如果需要 mock Nuxt 导入并在测试间提供不同实现,可以使用 vi.hoisted 创建并暴露 mock,然后在 mockNuxtImport 中使用这些 mock。这样你就可以访问被 mock 的导入,并在测试间更改实现。注意在每个测试之前或之后恢复 mock 以撤销运行间的 mock 状态更改。

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: 'something else' }
})

如果需要仅在测试内部 mock 行为,也可以使用以下方式。

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 允许你 mock Nuxt 的组件。 第一个参数可以是 PascalCase 的组件名,或组件的相对路径。 第二个参数是返回被 mock 组件的工厂函数。

例如,要 mock MyComponent,可以:

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

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

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

// 或者使用 SFC 重定向到 mock 组件
mockComponent('MyComponent', () => import('./MockComponent.vue'))

// 你的测试在这里

注意:不能在工厂函数中引用局部变量,因为它们是提升的。如果需要访问 Vue API 或其他变量,需要在工厂函数中导入。

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 允许你创建返回 mock 数据的 Nitro 端点。如果要测试向 API 发请求以显示数据的组件,这很方便。

第一个参数是端点名称(如 /test/)。 第二个参数是返回 mock 数据的工厂函数。

例如,要 mock /test/ 端点,可以:

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

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

默认情况下,请求使用 GET 方法。可以通过将第二个参数设为对象(而不是函数)来使用其他方法。

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

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

此对象接受以下属性:

  • handler:事件处理函数
  • method:(可选)要匹配的 HTTP 方法(如 'GET'、'POST')
  • once:(可选)如果为 true,handler 仅用于第一个匹配的请求,然后自动移除

注意:如果组件中的请求发往外部 API,可以使用 baseURL,然后通过 Nuxt 环境覆盖配置($test)将其设为空,这样所有请求都会发往 Nitro 服务器。

与端到端测试的冲突

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

如果想同时使用 @nuxt/test-utils 的端到端和单元测试功能,可以将测试拆分为单独的文件。然后要么用特殊的 // @vitest-environment nuxt 注释按文件指定测试环境,要么将运行时单元测试文件命名为 .nuxt.spec.ts 扩展名。

app.nuxt.spec.ts

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

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

app.e2e.spec.ts

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

await setup({
  setupTimeout: 10000,
})

// ...

使用 @vue/test-utils

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

  1. 安装所需依赖
npm i --save-dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue
yarn add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue
pnpm add -D vitest @vue/test-utils happy-dom @vitejs/plugin-vue
bun add --dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue
2. 创建 `vitest.config.ts`,内容如下: ```ts 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"
},
  1. 创建简单的 <HelloWorld> 组件 app/components/HelloWorld.vue,内容如下:
<template>
  <p>Hello world</p>
</template>
  1. 为新创建的组件创建简单的单元测试 ~/components/HelloWorld.spec.ts:
import { describe, expect, it } from 'vitest'
import { mount } from '@vue/test-utils'

import HelloWorld from './HelloWorld.vue'

describe('HelloWorld', () => {
  it('组件正确渲染 Hello world', () => {
    const wrapper = mount(HelloWorld)
    expect(wrapper.text()).toContain('Hello world')
  })
})
  1. 运行 vitest 命令
npm run test
yarn test
pnpm run test
bun run test

恭喜,你已经准备好在 Nuxt 中用 @vue/test-utils 开始单元测试了!测试愉快!

Vitest 浏览器模式

@nuxt/test-utils 通过 @nuxt/test-utils/browser 提供在 Vitest 浏览器模式中测试的辅助工具。

设置

  1. 安装 Vitest 浏览器包和浏览器提供器:
npm i --save-dev @vitest/browser-playwright
yarn add --dev @vitest/browser-playwright
pnpm add -D @vitest/browser-playwright
bun add --dev @vitest/browser-playwright
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' }], }, // 如果要启用 page.render 和自动清理,添加此 setup 文件 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/`),参见[添加其他测试目录](#添加其他测试目录)将它们添加到 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'
// 如果你添加了 setup 文件,不需要下面的导入
import '@nuxt/test-utils/browser'

import { MyCounter } from '#components'

it('计数器按钮递增计数', 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:

// @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('可以使用 render 辅助工具渲染', 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 示例。
  • container:自定义 HTMLElement 容器以渲染到其中(使用此选项代替 @vue/test-utils 的 attachTo)。
  • baseElement:自定义基础 HTMLElement(默认 document.body)。

返回对象包含以下属性:

  • container:组件渲染到的容器 HTMLElement。
  • baseElement:基础 HTMLElement(默认 document.body)。
  • locator:根元素 Locator。
  • setupState:组件 setup 的返回值,启用 spy 选项时被 mock。
  • debug():将格式化的 DOM 打印到控制台。
  • unmount():卸载组件。同时记录 nuxt.unmount 跟踪标记。
  • emitted():获取触发的事件。
  • rerender(props):用新 props 重新渲染组件。同时记录 nuxt.rerender 跟踪标记。

端到端测试

对于端到端测试,我们支持 Vitest、Jest、Cucumber 和 Playwright 作为测试运行器。

设置

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

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

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

  test('my 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,而不是构建和运行新服务器。用于对已部署版本的应用或已运行的本地服务器运行"真实"端到端测试很有用(可以显著减少测试执行时间)。参见下面的目标主机端到端示例。
    • 类型:string
    • 默认:undefined
  • browser:底层,Nuxt test utils 使用 playwright 执行浏览器测试。如果设置此选项,会启动浏览器并可在后续测试套件中控制。
    • 类型:boolean
    • 默认:false
  • browserOptions
    • 类型:object,包含以下属性
      • type:要启动的浏览器类型 - chromium、firefox 或 webkit
      • launch:启动浏览器时传给 playwright 的选项 object。参见完整 API 参考。
  • runner:指定测试套件的运行器。目前推荐 Vitest。
    • 类型:'vitest' | 'jest' | 'cucumber'
    • 默认:'vitest'
  • logLevel:覆盖服务器子进程的 consola 日志级别。(使用 NUXT_TEST_LOG_LEVEL 环境变量覆盖)
    • 类型:number
    • 默认:1
  • captureServerLogs:是否捕获服务器进程输出而不是继承 stdio。当 true(默认)时,服务器 stdout/stderr 从控制台抑制,可通过 getServerLogs() 访问。设为 false 恢复旧的继承 stdio 行为(本地调试测试时有用)。
    • 类型:boolean
    • 默认:true
目标 host 端到端示例

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

对于本地开发或自动化部署流水线,对单独的本地服务器测试可能更高效,通常比让测试框架在测试间重建更快。

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

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。

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

const html = await $fetch('/')

fetch(url)

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

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

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

url(path)

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

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

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

getServerLogs()

返回自上次 startServer() 调用(或 clearServerLogs())以来从服务器子进程的 stdout/stderr 捕获的行。仅在 captureServerLogs 为 true(默认)时有值。

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

it('捕获服务器路由的 console.log 输出', 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 文档中了解更多可用的 API 方法。

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

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

使用 Playwright 测试运行器测试

我们还在 Playwright 测试运行器中提供一流的 Nuxt 测试支持。

npm i --save-dev @playwright/test @nuxt/test-utils
yarn add --dev @playwright/test @nuxt/test-utils
pnpm add -D @playwright/test @nuxt/test-utils
bun add --dev @playwright/test @nuxt/test-utils
deno add --dev npm:@playwright/test npm:@nuxt/test-utils

可以提供全局 Nuxt 配置,与本节前面提到的 setup() 函数具有相同的配置细节。

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)),
    },
  },
  // ...
})

然后测试文件应直接从 @nuxt/test-utils/playwright 使用 expect 和 test:

import { expect, test } from '@nuxt/test-utils/playwright'

test('test', async ({ page, goto }) => {
  await goto('/', { waitUntil: 'hydration' })
  await expect(page.getByRole('heading')).toHaveText('Welcome to Playwright!')
})

或者直接在测试文件中配置 Nuxt 服务器:

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('Welcome to Playwright!')
})