透過 100 多個技巧的集合來學習 Nuxt!

測試

如何測試您的 Nuxt 應用程式。
如果您是模組作者,您可以在模組作者指南中找到更詳細的資訊。

Nuxt 透過 @nuxt/test-utils 為您的 Nuxt 應用程式提供一流的端對端和單元測試支援,@nuxt/test-utils 是一個測試工具和設定的函式庫,目前為 Nuxt 本身使用的測試以及整個模組生態系統的測試提供支援。

觀看 Alexander Lichter 關於 @nuxt/test-utils 入門的影片。

安裝

為了讓您管理其他測試依賴項,@nuxt/test-utils 附帶各種可選的同級依賴項。例如

  • 您可以選擇 happy-domjsdom 之間的一個作為執行時 Nuxt 環境
  • 您可以為端對端測試執行器選擇 vitestcucumberjestplaywright 之間的一個
  • playwright-core 僅在您希望使用內建瀏覽器測試工具(且未使用 @playwright/test 作為測試執行器)時才需要
npm i --save-dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core

單元測試

我們目前提供一個環境,用於單元測試需要 Nuxt 執行時環境的程式碼。目前僅支援 vitest(雖然歡迎貢獻以新增其他執行時環境)。

設定

  1. @nuxt/test-utils/module 新增至您的 nuxt.config 檔案(可選)。它會將 Vitest 整合新增至您的 Nuxt DevTools,以支援在開發中執行單元測試。
    export default 
    defineNuxtConfig
    ({
    modules
    : [
    '@nuxt/test-utils/module' ] })
  2. 建立一個包含以下內容的 vitest.config.ts
    import { 
    defineVitestConfig
    } from '@nuxt/test-utils/config'
    export default
    defineVitestConfig
    ({
    // any custom Vitest config you require })
在您的 vitest 設定中匯入 @nuxt/test-utils 時,必須在您的 package.json 中指定 "type": "module",或適當地重新命名您的 vitest 設定檔。

例如 vitest.config.m{ts,js}

可以使用 .env.test 檔案為測試設定環境變數。

使用 Nuxt 執行時環境

預設情況下,@nuxt/test-utils 不會變更您的預設 Vitest 環境,因此您可以進行細粒度的選擇加入,並將 Nuxt 測試與其他單元測試一起執行。

您可以透過在測試檔案名稱中新增 .nuxt.(例如,my-file.nuxt.test.tsmy-file.nuxt.spec.ts)或直接在測試檔案中新增 @vitest-environment nuxt 作為註解,來選擇加入 Nuxt 環境。

// @vitest-environment nuxt
import { 
test
} from 'vitest'
test
('my test', () => {
// ... test with Nuxt environment! })

或者,您可以在 Vitest 設定中設定 environment: 'nuxt',以針對所有測試啟用 Nuxt 環境。

// vitest.config.ts
import { 
fileURLToPath
} from 'node:url'
import {
defineVitestConfig
} from '@nuxt/test-utils/config'
export default
defineVitestConfig
({
test
: {
environment
: 'nuxt',
// you can optionally set Nuxt-specific environment options // environmentOptions: { // nuxt: { // rootDir: fileURLToPath(new URL('./playground', import.meta.url)), // domEnvironment: 'happy-dom', // 'happy-dom' (default) or 'jsdom' // overrides: { // // other Nuxt config you want to pass // } // } // } } })

如果您預設已設定 environment: 'nuxt',則可以根據需要選擇**退出**每個測試檔案的預設環境

// @vitest-environment node
import { 
test
} from 'vitest'
test
('my test', () => {
// ... test without Nuxt environment! })
當您在 Nuxt 環境中執行測試時,它們將在 happy-domjsdom 環境中執行。在您的測試執行之前,將會初始化全域 Nuxt 應用程式(包括,例如,執行您在 app.vue 中定義的任何外掛程式或程式碼)。這表示您應特別注意不要在測試中變更全域狀態(或者,如果需要,請在之後重設它)。

🎭 內建模擬

@nuxt/test-utils 為 DOM 環境提供了一些內建模擬。

intersectionObserver

預設值 true,為 IntersectionObserver API 建立一個沒有任何功能的虛擬類別

indexedDB

預設值 false,使用 fake-indexeddb 建立 IndexedDB API 的功能模擬

這些可以在您的 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 元件,允許非同步設定和存取來自 Nuxt 外掛程式的注入。

在底層,mountSuspended 包裝了來自 @vue/test-utilsmount,因此您可以查看 Vue Test Utils 文件,以了解您可以傳遞的選項以及如何使用此工具的更多資訊。

例如

// tests/components/SomeComponents.nuxt.spec.ts
import { 
mountSuspended
} from '@nuxt/test-utils/runtime'
import {
SomeComponent
} from '#components'
it
('can mount some component', async () => {
const
component
= await
mountSuspended
(
SomeComponent
)
expect
(
component
.
text
()).
toMatchInlineSnapshot
(
'"This is an auto-imported component"' ) })
// tests/components/SomeComponents.nuxt.spec.ts
import { 
mountSuspended
} from '@nuxt/test-utils/runtime'
import
App
from '~/app.vue'
// tests/App.nuxt.spec.ts
it
('can also mount an app', 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>" `) })

renderSuspended

renderSuspended 允許您使用 @testing-library/vue 在 Nuxt 環境中渲染任何 Vue 元件,允許非同步設定和存取來自 Nuxt 外掛程式的注入。

這應該與 Testing Library 的工具一起使用,例如 screenfireEvent。在您的專案中安裝 @testing-library/vue 以使用這些工具。

此外,Testing Library 也依賴測試全域變數進行清理。您應該在您的 Vitest 設定 中開啟這些變數。

傳入的元件將在 <div id="test-wrapper"></div> 內渲染。

範例

// tests/components/SomeComponents.nuxt.spec.ts
import { 
renderSuspended
} from '@nuxt/test-utils/runtime'
import {
SomeComponent
} from '#components'
import {
screen
} from '@testing-library/vue'
it
('can render some component', async () => {
await
renderSuspended
(
SomeComponent
)
expect
(
screen
.
getByText
('This is an auto-imported component')).
toBeDefined
()
})
// tests/App.nuxt.spec.ts
import { 
renderSuspended
} from '@nuxt/test-utils/runtime'
import
App
from '~/app.vue'
it
('can also render an app', 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>" `) })

mockNuxtImport

mockNuxtImport 允許您模擬 Nuxt 的自動匯入功能。例如,若要模擬 useStorage,您可以這樣做

import { 
mockNuxtImport
} from '@nuxt/test-utils/runtime'
mockNuxtImport
('useStorage', () => {
return () => { return {
value
: 'mocked storage' }
} }) // your tests here
mockNuxtImport 每個測試檔案中每個模擬匯入只能使用一次。它實際上是一個巨集,會轉換為 vi.mock,而 vi.mock 會被提升,如 此處 所述。

如果您需要模擬 Nuxt 匯入並在測試之間提供不同的實作,您可以透過使用 vi.hoisted 建立和公開您的模擬來完成,然後在 mockNuxtImport 中使用這些模擬。然後,您可以存取模擬的匯入,並在測試之間變更實作。請務必在每次測試之前或之後還原模擬,以還原執行之間模擬狀態的變更。

import { 
vi
} from 'vitest'
import {
mockNuxtImport
} from '@nuxt/test-utils/runtime'
const {
useStorageMock
} =
vi
.
hoisted
(() => {
return {
useStorageMock
:
vi
.
fn
(() => {
return {
value
: 'mocked storage'}
}) } })
mockNuxtImport
('useStorage', () => {
return useStorageMock }) // Then, inside a test
useStorageMock
.
mockImplementation
(() => {
return {
value
: 'something else' }
})

mockComponent

mockComponent 允許您模擬 Nuxt 的元件。第一個引數可以是 PascalCase 中的元件名稱,或元件的相對路徑。第二個引數是回傳模擬元件的工廠函式。

例如,若要模擬 MyComponent,您可以

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

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

// relative path or alias also works
mockComponent('~/components/my-component.vue', async () => {
  // or a factory function
  return defineComponent({
    setup(props) {
      // ...
    }
  })
})

// or you can use SFC for redirecting to a mock component
mockComponent('MyComponent', () => import('./MockComponent.vue'))

// your tests here

注意:您無法在工廠函式中參考區域變數,因為它們會被提升。如果您需要存取 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 允許您建立回傳模擬資料的 Nitro 端點。如果您想測試一個向 API 發出請求以顯示某些資料的元件,它可能會派上用場。

第一個引數是端點名稱(例如 /test/)。第二個引數是回傳模擬資料的工廠函式。

例如,若要模擬 /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' })
})

注意:如果您的元件中的請求發送到外部 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
('useStorage', () => {
return () => { return {
value
: 'mocked storage' }
} })

app.e2e.spec.ts

import { 
setup
,
$fetch
} from '@nuxt/test-utils/e2e'
await
setup
({
setupTimeout
: 10000,
}) // ...

使用 @vue/test-utils

如果您偏好單獨使用 @vue/test-utils 在 Nuxt 中進行單元測試,並且您僅測試不依賴 Nuxt composable、自動匯入或內容的元件,則可以按照這些步驟進行設定。

  1. 安裝所需的依賴項
    npm i --save-dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue
    
  2. 建立一個包含以下內容的 vitest.config.ts
    import { 
    defineConfig
    } from 'vitest/config'
    import
    vue
    from '@vitejs/plugin-vue'
    export default
    defineConfig
    ({
    plugins
    : [
    vue
    ()],
    test
    : {
    environment
    : 'happy-dom',
    }, });
  3. 在您的 package.json 中新增一個新的測試命令
    "scripts": {
      "build": "nuxt build",
      "dev": "nuxt dev",
      ...
      "test": "vitest"
    },
    
  4. 建立一個簡單的 <HelloWorld> 元件 components/HelloWorld.vue,其中包含以下內容
    <template>
      <p>Hello world</p>
    </template>
    
  5. 為這個新建立的元件建立一個簡單的單元測試 ~/components/HelloWorld.spec.ts
    import { describe, it, expect } from 'vitest'
    import { mount } from '@vue/test-utils'
    
    import HelloWorld from './HelloWorld.vue'
    
    describe('HelloWorld', () => {
      it('component renders Hello world properly', () => {
        const wrapper = mount(HelloWorld)
        expect(wrapper.text()).toContain('Hello world')
      })
    })
    
  6. 執行 vitest 命令
    npm run test
    

恭喜,您已準備好開始在 Nuxt 中使用 @vue/test-utils 進行單元測試!祝您測試愉快!

端對端測試

對於端對端測試,我們支援 Vitest、Jest、Cucumber 和 Playwright 作為測試執行器。

設定

在您利用 @nuxt/test-utils/e2e 輔助方法的每個 describe 區塊中,您都需要在開始之前設定測試內容。

test/my-test.spec.ts
import { 
describe
,
test
} from 'vitest'
import {
setup
,
$fetch
} from '@nuxt/test-utils/e2e'
describe
('My test', async () => {
await
setup
({
// test context options })
test
('my test', () => {
// ... }) })

在幕後,setup 會在 beforeAllbeforeEachafterEachafterAll 中執行多項任務,以正確設定 Nuxt 測試環境。

請為 setup 方法使用以下選項。

Nuxt 設定

  • rootDir:要測試的 Nuxt 應用程式目錄的路徑。
    • 類型:string
    • 預設值:'.'
  • configFile:設定檔的名稱。
    • 類型:string
    • 預設值:'nuxt.config'

計時

  • setupTimeout:允許 setupTest 完成其工作所需的時間量(以毫秒為單位)(其中可能包括建置或產生 Nuxt 應用程式的檔案,具體取決於傳遞的選項)。
    • 類型:number
    • 預設值:60000

功能

  • build:是否執行單獨的建置步驟。
    • 類型:boolean
    • 預設值:true(如果停用 browserserver,或如果提供 host,則為 false
  • server:是否啟動伺服器以回應測試套件中的請求。
    • 類型:boolean
    • 預設值:true(如果提供 host,則為 false
  • port:如果提供,將啟動的測試伺服器埠設定為該值。
    • 類型:number | undefined
    • 預設值:undefined
  • host:如果提供,則使用 URL 作為測試目標,而不是建置和執行新的伺服器。適用於針對已部署版本的應用程式或針對已執行的本機伺服器執行「真實」的端對端測試(這可能會顯著減少測試執行時間)。請參閱下方的目標主機端對端範例
    • 類型:string
    • 預設值:undefined
  • browser:在底層,Nuxt 測試工具使用 playwright 執行瀏覽器測試。如果設定此選項,則將啟動瀏覽器,並可在後續的測試套件中控制該瀏覽器。
    • 類型:boolean
    • 預設值:false
  • browserOptions
    • 類型:具有以下屬性的 object
      • type:要啟動的瀏覽器類型 - chromiumfirefoxwebkit
      • launch:啟動瀏覽器時將傳遞給 playwright 的選項 object。請參閱完整的 API 參考
  • runner:指定測試套件的執行器。目前,建議使用 Vitest
    • 類型:'vitest' | 'jest' | 'cucumber'
    • 預設值:'vitest'
目標主機端對端範例

端對端測試的常見用例是對在與通常用於生產環境相同的環境中執行的已部署應用程式執行測試。

對於本機開發或自動化部署管道,針對單獨的本機伺服器進行測試可能更有效率,並且通常比允許測試框架在測試之間重建更快。

若要為端對端測試使用單獨的目標主機,只需提供 setup 函式的 host 屬性以及所需的 URL。

import { 
setup
,
createPage
} from '@nuxt/test-utils/e2e'
import {
describe
,
it
,
expect
} from 'vitest'
describe
('login page', async () => {
await
setup
({
host
: 'https://127.0.0.1:8787',
})
it
('displays the email and password fields', 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')
// 'https://127.0.0.1:6840/page'

在瀏覽器中測試

我們提供使用 Playwright 在 @nuxt/test-utils 內進行內建支援,無論是以程式方式還是透過 Playwright 測試執行器。

createPage(url)

vitestjestcucumber 中,您可以使用 createPage 建立已設定的 Playwright 瀏覽器實例,並(可選)將其指向執行中伺服器的路徑。您可以從 Playwright 文件中找到有關可用 API 方法的更多資訊。

import { 
createPage
} from '@nuxt/test-utils/e2e'
const
page
= await
createPage
('/page')
// you can access all the Playwright APIs from the `page` variable

使用 Playwright 測試執行器進行測試

我們也為在 Playwright 測試執行器中測試 Nuxt 提供一流的支援。

npm i --save-dev @playwright/test @nuxt/test-utils

您可以提供全域 Nuxt 設定,其設定詳細資訊與本節前面提到的 setup() 函式相同。

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))
    }
  },
  // ...
})
在「查看完整範例設定」中閱讀更多資訊。

然後,您的測試檔案應直接從 @nuxt/test-utils/playwright 使用 expecttest

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

或者,您可以直接在測試檔案中設定您的 Nuxt 伺服器

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