テスト

Nuxt アプリケーションをテストする方法。
モジュール作成者は、モジュール作成者のガイドでより詳細な情報を見つけることができます。

Nuxt は、@nuxt/test-utils を介して、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',
            },
          }),
        ],
      },
    })
    
vitest 設定で @nuxt/test-utils をインポートする場合、package.json"type": "module" を指定するか、vitest 設定ファイルを適切に名前変更する必要があります。

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

.env.test ファイルを使用して、テスト用の環境変数を設定できます。

Nuxt ランタイム環境の使用

Vitest プロジェクトを使用すると、Vitest プロジェクトどの環境でどのテストを実行するかをきめ細かく制御できます。

  • 単体テスト: 通常の単体テストは test/unit/ に配置します。これらは速度のために Node 環境で実行されます。
  • Nuxt テスト: Nuxt ランタイム環境に依存するテストは test/nuxt/ に配置します。これらは Nuxt ランタイム環境内で実行されます。

代替: シンプルな設定

よりシンプルな設定を好み、すべてのテストを Nuxt 環境で実行したい場合は、基本的な設定を使用できます。

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' を使用したシンプルな設定を使用している場合、必要に応じてテストファイルごとに Nuxt 環境を オプトアウト できます。Nuxt 環境を必要に応じてテストファイルごとに設定できます。

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

test('my test', () => {
  // ... test without Nuxt environment!
})
このアプローチは、Nuxt Vite プラグインが実行されるが、Nuxt のエントリと nuxtApp が初期化されないハイブリッド環境を作成するため、推奨されません。これにより、デバッグが困難なエラーが発生する可能性があります。

テストの整理

プロジェクトベースの設定では、テストを次のように整理できます。

ディレクトリ構造
test/
├── e2e/
   └── ssr.test.ts
├── nuxt/
   ├── components.test.ts
   └── composables.test.ts
├── unit/
   └── utils.test.ts

もちろん、どのようなテスト構造を選択することもできますが、Nuxt ランタイム環境を Nuxt エンドツーエンドテストから分離することは、テストの安定性のために重要です。

テストの実行

プロジェクト設定では、異なるテストスイートを実行できます。

# Run all tests
npx vitest

# Run only unit tests
npx vitest --project unit

# Run only Nuxt tests
npx vitest --project nuxt

# Run tests in watch mode
npx vitest --watch
Nuxt 環境内でテストを実行すると、テストはhappy-domまたはjsdom環境で実行されます。テストが実行される前に、グローバル Nuxt アプリが初期化されます (たとえば、app.vue で定義したプラグインやコードの実行を含む)。これは、テストでグローバルな状態を変更しないように (または、変更する必要がある場合は、後でリセットするように) 特に注意する必要があることを意味します。

🎭 組み込みモック

@nuxt/test-utils は、DOM 環境用のいくつかの組み込みモックを提供します。

intersectionObserver

デフォルト true、IntersectionObserver API の機能を持たないダミークラスを作成します。

indexedDB

デフォルト falsefake-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 config.

でオンにする必要があります。

渡されたコンポーネントは <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 は、Vitest ドキュメント.

に記載されているように、ホイストされます。 Nuxt インポートをモックし、テスト間で異なる実装を提供する必要がある場合は、vi.hoisted を使用してモックを作成および公開し、そのモックを mockNuxtImport で使用することで実現できます。これにより、モックされたインポートにアクセスでき、テスト間で実装を変更できます。実行間でモックの状態変更を元に戻すために、各テストの前後で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) {
    // ...
  },
})

// relative path or alias also works
mockComponent('~/components/my-component.vue', () => {
  // 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 { $fetch, setup } 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, expect, it } 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, CucumberGlobalComponentsPlaywrightをテストランナーとしてサポートしています。

セットアップ

@nuxt/test-utils/e2e ヘルパーメソッドを利用する各 describe ブロックでは、開始する前にテストコンテキストを設定する必要があります。

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

describe('My test', async () => {
  await setup({
    // test context options
  })

  test('my test', () => {
    // ...
  })
})

内部では、setup は、beforeAllbeforeEachafterEach、および 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 テストユーティリティはplaywrightを使用してブラウザテストを実行します。このオプションが設定されている場合、ブラウザが起動され、後続のテストスイートで制御できます。
    • 型: boolean
    • デフォルト: false
  • browserOptions
    • 型: 次のプロパティを持つ object
      • type: 起動するブラウザの種類 - chromiumfirefox、または webkit のいずれか。
      • launch: ブラウザ起動時に playwright に渡されるオプションの object。詳細は完全な API リファレンス.
  • を参照してください。Vitestrunner: テストスイートのランナーを指定します。現在、
    • が推奨されています。
    • 型: 'vitest' | 'jest' | 'cucumber'
デフォルト: 'vitest'

ターゲット host エンドツーエンドの例

エンドツーエンドテストの一般的な使用例は、本番環境で通常使用されるのと同じ環境で実行されているデプロイ済みアプリケーションに対してテストを実行することです。

ローカル開発や自動デプロイパイプラインでは、個別のローカルサーバーに対してテストを実行する方が効率的であり、通常、テストフレームワークがテスト間で再構築するよりも高速です。

import { createPage, setup } from '@nuxt/test-utils/e2e'
import { describe, expect, it } from 'vitest'

describe('login page', async () => {
  await setup({
    host: 'https://: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)
  })
})

エンドツーエンドテストのために個別のターゲットホストを利用するには、setup 関数の host プロパティに目的の URL を指定するだけです。

API

$fetch(url)

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

const html = await $fetch('/')

サーバーレンダリングされたページの HTML を取得します。

fetch(url)

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

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

サーバーレンダリングされたページからのレスポンスを取得します。

url(path)

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

const pageUrl = url('/page')
// 'https://:6840/page'

特定のページの完全な URL (テストサーバーが実行されているポートを含む) を取得します。

ブラウザでのテスト

@nuxt/test-utils 内では、プログラム的または Playwright テストランナーを介して、Playwright を使用した組み込みサポートが提供されています。

createPage(url)vitestjest、または cucumber 内では、createPage で設定された Playwright ブラウザインスタンスを作成し、(オプションで) 実行中のサーバーからのパスを指定できます。利用可能な API メソッドの詳細については、Playwright ドキュメントを参照してください。.

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 テストランナーでのテストまた、Playwright テストランナー内での Nuxt のテストもファーストクラスでサポートしています。.

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