測試
Nuxt 透過 @nuxt/test-utils
,提供對 Nuxt 應用程式端對端和單元測試的一流支援。這是一個測試工具和配置的函式庫,目前為 我們在 Nuxt 本身使用的測試以及整個模組生態系統的測試提供動力。
安裝
為了讓您管理其他測試相依性,@nuxt/test-utils
附帶各種可選的對等相依性。例如
- 您可以選擇
happy-dom
和jsdom
作為 Nuxt 執行環境 - 您可以選擇
vitest
、cucumber
、jest
和playwright
作為端對端測試執行器 - 如果您希望使用內建的瀏覽器測試工具(並且未使用
@playwright/test
作為您的測試執行器),則只需要playwright-core
npm i --save-dev @nuxt/test-utils vitest @vue/test-utils happy-dom playwright-core
單元測試
我們目前提供一個用於單元測試的環境,該環境需要 Nuxt 執行時環境。目前僅支援 vitest
(雖然歡迎貢獻以添加其他運行時環境)。
設定
- 將
@nuxt/test-utils/module
添加到你的nuxt.config
檔案(可選)。它會將 Vitest 整合到你的 Nuxt DevTools 中,支援在開發環境中執行單元測試。export default
defineNuxtConfig({modules: [ '@nuxt/test-utils/module' ] }) - 建立一個包含以下內容的
vitest.config.ts
檔案import {
defineVitestConfig} from '@nuxt/test-utils/config' export defaultdefineVitestConfig({ // any custom Vitest config you require })
@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.ts
或 my-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!
})
happy-dom
或 jsdom
環境中執行。在你的測試執行之前,將會初始化一個全域 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-utils
中的 mount
,因此你可以查看 Vue Test Utils 文件以了解更多關於你可以傳遞的選項,以及如何使用這個工具。例如
import { it, expect } from 'vitest'
import type { Component } from 'vue'
declare module '#components' {
export const SomeComponent: Component
}
// ---cut---
// 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"'
)
})
import { it, expect } from 'vitest'
// ---cut---
// 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 的工具一起使用,例如 screen
和 fireEvent
。在你的專案中安裝 @testing-library/vue 以使用這些工具。
此外,Testing Library 也依賴測試全域變數進行清理。你應該在你的 Vitest 設定中開啟這些變數。
傳入的元件將會被渲染在 <div id="test-wrapper"></div>
裡面。
範例
import { it, expect } from 'vitest'
import type { Component } from 'vue'
declare module '#components' {
export const SomeComponent: Component
}
// ---cut---
// 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()
})
import { it, expect } from 'vitest'
// ---cut---
// 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
如果你需要在測試之間模擬 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
注意:你不能在工廠函數中引用本地變數,因為它們會被提升(hoisted)。如果你需要存取 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 組合式函式、自動匯入或上下文的元件,你可以按照以下步驟進行設定。
- 安裝所需的依賴項
npm i --save-dev vitest @vue/test-utils happy-dom @vitejs/plugin-vue
- 建立一個包含以下內容的
vitest.config.ts
檔案import { defineConfig } from 'vitest/config' import vue from '@vitejs/plugin-vue' export default defineConfig({ plugins: [vue()], test: { environment: 'happy-dom', }, });
- 在你的
package.json
中新增一個測試指令"scripts": { "build": "nuxt build", "dev": "nuxt dev", ... "test": "vitest" },
- 使用以下內容建立一個簡單的
<HelloWorld>
元件components/HelloWorld.vue
<template> <p>Hello world</p> </template>
- 為這個新建立的元件建立一個簡單的單元測試
~/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') }) })
- 執行 vitest 指令
npm run test
恭喜你,你已準備好開始在 Nuxt 中使用 @vue/test-utils
進行單元測試!祝你測試愉快!
端對端測試
對於端對端測試,我們支援 Vitest、Jest、Cucumber 和 Playwright 作為測試執行器。
設定
在你使用 @nuxt/test-utils/e2e
輔助方法的每個 describe
區塊中,你需要先設定測試上下文才能開始。
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
會在 beforeAll
、beforeEach
、afterEach
和 afterAll
中執行許多任務,以正確設定 Nuxt 測試環境。
請使用以下選項來設定 setup
方法。
Nuxt 設定
rootDir
:要測試的 Nuxt 應用程式所在目錄的路徑。- 類型:
string
- 預設:
'.'
- 類型:
configFile
:設定檔的名稱。- 類型:
string
- 預設:
'nuxt.config'
- 類型:
計時
setupTimeout
:允許setupTest
完成其工作(可能包括建置或產生 Nuxt 應用程式的檔案,具體取決於傳遞的選項)的時間量(以毫秒為單位)。- 類型:
number
- 預設:
60000
- 類型:
功能
build
:是否執行單獨的建置步驟。- 類型:
boolean
- 預設:
true
(如果browser
或server
被停用,或如果提供了host
,則為false
)
- 類型:
server
:是否啟動伺服器來響應測試套件中的請求。- 類型:
boolean
- 預設:
true
(如果提供了host
,則為false
)
- 類型:
port
:如果提供,將啟動的測試伺服器連接埠設定為該值。- 類型:
number | undefined
- 預設:
undefined
- 類型:
host
:如果提供,則使用一個 URL 作為測試目標,而不是建置和執行新的伺服器。適用於針對你應用程式的已部署版本,或針對已在運行的本地伺服器執行「真實」端對端測試(這可能會顯著減少測試執行時間)。請參閱下方的 目標主機端對端範例。- 類型:
string
- 預設:
undefined
- 類型:
browser
:在幕後,Nuxt 測試工具使用playwright
來執行瀏覽器測試。如果設定了此選項,將會啟動瀏覽器,並且可以在後續的測試套件中控制它。- 類型:
boolean
- 預設:
false
- 類型:
browserOptions
- 類型:具有以下屬性的
object
type
:要啟動的瀏覽器類型 - 可以是chromium
、firefox
或webkit
。launch
:將在啟動瀏覽器時傳遞給 playwright 的選項object
。請參閱完整的 API 參考資料。
- 類型:具有以下屬性的
runner
:指定測試套件的執行器。目前建議使用 Vitest。- 類型:
'vitest' | 'jest' | 'cucumber'
- 預設值:
'vitest'
- 類型:
目標 host
端對端範例
端對端測試的常見使用案例是針對通常用於生產環境的已部署應用程式執行測試。
對於本地開發或自動部署管道,針對單獨的本地伺服器進行測試可能更有效率,並且通常比允許測試框架在測試之間重建更快。
若要針對端對端測試使用單獨的目標主機,只需在 setup
函式中提供所需的 URL 作為 host
屬性即可。
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)
$fetch(url)
取得伺服器渲染頁面的 HTML。
import { $fetch } from '@nuxt/test-utils/e2e'
const html = await $fetch('/')
fetch(url)
fetch(url)
取得伺服器渲染頁面的回應。
import { fetch } from '@nuxt/test-utils/e2e'
const res = await fetch('/')
const { body, headers } = res
url(path)
url(path)
取得指定頁面的完整 URL (包括測試伺服器正在執行的埠)。
import { url } from '@nuxt/test-utils/e2e'
const pageUrl = url('/page')
// 'https://127.0.0.1:6840/page'
在瀏覽器中測試
我們在 @nuxt/test-utils
中提供使用 Playwright 的內建支援,可以透過程式設計方式或透過 Playwright 測試執行器來使用。
createPage(url)
createPage(url)
在 vitest
、jest
或 cucumber
中,您可以使用 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()
函式相同。
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!')
})