nuxt logo

ドキュメント翻訳(非公式)

テスト

Nuxtアプリケーションのテスト方法。

モジュールの作者であれば、モジュール作者向けガイドでより具体的な情報を見つけることができます。

Nuxtは、@nuxt/test-utilsを通じてNuxtアプリケーションのエンドツーエンドおよびユニットテストを一流のサポートで提供します。このライブラリは、現在Nuxt自体で使用しているテストやモジュールエコシステム全体のテストを支えています。

インストール

他のテスト依存関係を管理できるようにするために、@nuxt/test-utilsはさまざまなオプションのピア依存関係を提供します。例えば:

  • 実行時のNuxt環境としてhappy-domjsdomのどちらかを選択できます
  • エンドツーエンドテストランナーとして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/modulenuxt.configファイルに追加します(オプション)。これにより、開発中にユニットテストを実行するためのVitest統合がNuxt DevToolsに追加されます。

    export default defineNuxtConfig({
      modules: [
        '@nuxt/test-utils/module'
      ]
    })
    
  2. 次の内容で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/{e2e,unit}/*.{test,spec}.ts'],
              environment: 'node',
            },
          },
          await defineVitestProject({
            test: {
              name: 'nuxt',
              include: ['test/nuxt/*.{test,spec}.ts'],
              environment: 'nuxt',
            },
          }),
        ],
      },
    })
    

@nuxt/test-utilsをvitestの設定でインポートする際には、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'

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エンドツーエンドテストから分離しておくことがテストの安定性にとって重要です。

テストの実行

プロジェクトセットアップを使用すると、異なるテストスイートを実行できます:

# すべてのテストを実行
npx vitest

# ユニットテストのみを実行
npx vitest --project unit

# Nuxtテストのみを実行
npx vitest --project nuxt

# ウォッチモードでテストを実行
npx vitest --watch

Nuxt環境内でテストを実行すると、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-utilsmountをラップしているため、渡せるオプションやこのユーティリティの使用方法についてはVue Test Utilsのドキュメントを参照してください。

例えば:

// @noErrors
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"'
    )
})

// @noErrors
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のユーティリティ(例:screenfireEvent)と一緒に使用する必要があります。これらを使用するには、プロジェクトに@testing-library/vueをインストールしてください。

さらに、Testing Libraryはクリーンアップのためにテストグローバルに依存しています。これらをオンにするには、Vitestの設定で設定してください。

渡されたコンポーネントは<div id="test-wrapper"></div>内にレンダリングされます。

例:

// @noErrors
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()
})
// @noErrors
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は、テストファイルごとにモックされたインポートごとに一度だけ使用できます。実際にはvi.mockに変換され、vi.mockVitestのドキュメントで説明されているようにホイストされます。

テスト間で異なる実装を提供するために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のコンポーネント名、またはコンポーネントの相対パスです。 2番目の引数はモックされたコンポーネントを返すファクトリ関数です。

例えば、MyComponentをモックするには:

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

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

// 相対パスやエイリアスも機能します
mockComponent('~/components/my-component.vue', async () => {
  // またはファクトリ関数
  return defineComponent({
    setup(props) {
      // ...
    }
  })
})

// または、モックコンポーネントへのリダイレクトにSFCを使用できます
mockComponent('MyComponent', () => import('./MockComponent.vue'))

// your tests here

Note: ファクトリ関数内でローカル変数を参照することはできません。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' })
})

Note: コンポーネント内のリクエストが外部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のコンポーザブル、自動インポート、コンテキストに依存しないコンポーネントのみをテストする場合は、次の手順に従ってセットアップできます。

  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>コンポーネントapp/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を使用したユニットテストを始める準備が整いました!テストを楽しんでください!

エンドツーエンドテスト

エンドツーエンドテストには、テストランナーとしてVitestJestCucumberPlaywrightをサポートしています。

セットアップ

@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('my test', () => {
    // ...
  })
})

内部的には、setupbeforeAllbeforeEachafterEachafterAllで多くのタスクを実行し、Nuxtテスト環境を正しくセットアップします。

以下のオプションをsetupメソッドに使用してください。

Nuxt設定

  • rootDir: テスト対象のNuxtアプリを含むディレクトリへのパス。
    • タイプ: string
    • デフォルト: '.'
  • configFile: 設定ファイルの名前。
    • タイプ: string
    • デフォルト: 'nuxt.config'

タイミング

  • setupTimeout: setupTestが作業を完了するために許可される時間(ミリ秒単位)。これには、渡されたオプションに応じて、Nuxtアプリケーションのビルドやファイル生成が含まれる場合があります。

    • タイプ: number
    • デフォルト: 120000または240000(Windowsの場合)
  • teardownTimeout: テスト環境を解体するために許可される時間(ミリ秒単位)。例えば、ブラウザを閉じるなど。

    • タイプ: number
    • デフォルト: 30000

機能

  • build: 別のビルドステップを実行するかどうか。

    • タイプ: boolean
    • デフォルト: truebrowserまたはserverが無効、またはhostが提供されている場合はfalse
  • server: テストスイート内でリクエストに応答するためのサーバーを起動するかどうか。

    • タイプ: boolean
    • デフォルト: truehostが提供されている場合は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'
ターゲット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)

vitestjestcucumber内で、createPageを使用して設定済みのPlaywrightブラウザインスタンスを作成し、(オプションで)実行中のサーバーからのパスにポイントします。利用可能なAPIメソッドの詳細はPlaywrightのドキュメントで確認できます。

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

const page = await createPage('/page')
// `page`変数からすべてのPlaywright APIにアクセスできます

Playwrightテストランナーを使用したテスト

Playwrightテストランナー内でNuxtをテストするための一流のサポートも提供しています。

::code-group{sync="pm"}

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

::

前述のsetup()関数と同じ設定詳細を使用して、グローバルなNuxt設定を提供できます。

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!')
})