テスト
Nuxtは、Nuxtアプリケーションのエンドツーエンドテストとユニットテストを、テストユーティリティと設定のライブラリである@nuxt/test-utils
を通じて、ファーストクラスのサポートを提供します。これは現在、Nuxt自体で使用しているテストと、モジュールエコシステム全体のテストを支えています。
インストール
他のテスト依存関係を管理できるように、@nuxt/test-utils
は、さまざまなオプションのピア依存関係を同梱しています。たとえば、
- Nuxtランタイム環境には、
happy-dom
とjsdom
のどちらかを選択できます。 - エンドツーエンドテストランナーには、
vitest
、cucumber
、jest
、playwright
のいずれかを選択できます。 playwright-core
は、組み込みのブラウザテストユーティリティを使用する場合にのみ必要です(テストランナーとして@playwright/test
を使用していない場合)。
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のドキュメントを参照できます。例:
// 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>
内にレンダリングされます。
サンプル
// 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
mockNuxtImport
は、テストファイルごとにモックされたインポートに対して1回しか使用できません。これは実際には、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のコンポーネントをモックできます。最初の引数は、パスカルケースのコンポーネント名、またはコンポーネントの相対パスを指定できます。2番目の引数は、モックされたコンポーネントを返すファクトリ関数です。
たとえば、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/
)。2番目の引数は、モックされたデータを返すファクトリ関数です。
たとえば、/test/
エンドポイントをモックするには、次のようにします。
import { registerEndpoint } from '@nuxt/test-utils/runtime'
registerEndpoint('/test/', () => ({
test: 'test-field'
}))
デフォルトでは、リクエストはGET
メソッドを使用して行われます。関数の代わりにオブジェクトを2番目の引数として設定することにより、別のメソッドを使用できます。
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
を使用する
Nuxtのユニットテストに@vue/test-utils
を単独で使用することを好み、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
は、Nuxtテスト環境を正しく設定するために、beforeAll
、beforeEach
、afterEach
、およびafterAll
でいくつかのタスクを実行します。
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
関数のhost
プロパティに目的のURLを指定するだけです。
import { setup, createPage } from '@nuxt/test-utils/e2e'
import { describe, it, expect } from 'vitest'
describe('login page', async () => {
await setup({
host: 'http://localhost: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')
// 'http://localhost:6840/page'
ブラウザでのテスト
@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')
// you can access all the Playwright APIs from the `page` variable
Playwrightテストランナーを使用したテスト
Playwrightテストランナー内でのNuxtのテストに対するファーストクラスサポートも提供しています。
npm i --save-dev @playwright/test @nuxt/test-utils
このセクションの前述のsetup()
関数と同じ設定詳細を使用して、グローバルなNuxt設定を提供できます。
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!')
})