N-LAB

Nuxt 3 × Vitest × Vue Testing Libraryで単体テストの実行とカバレッジを取得する

更新日: 2024年12月28日


目標


前提

※本手順書ではソースディレクトリを「src」に変更している想定としています。

・Nuxt: 3.15.0
・Vitest 2.1.8
・Vue Testing Library 8.1.0
・happy-dom 16.0.1
・unplugin-auto-import 0.19.0
・unplugin-vue-components 0.28.0
・vitest/coverage-v8 2.1.8

目次

  1. Vitestのインストールと初期設定
  2. 自動インポート設定
  3. カバレッジの取得


Vitestのインストールと初期設定

1. Vitestをインストールします。併せてuser-eventVue Testing Library・happy-domもインストールします。

$ npm install --save-dev vitest @testing-library/user-event @testing-library/vue happy-dom


2. プロジェクトのルート配下に「vitest.config.ts」を新規作成し、以下の内容で保存します。

import path from 'path'
import Vue from '@vitejs/plugin-vue'
import { defineConfig } from 'vitest/config'

export default defineConfig({
  plugins: [
    Vue(),
  ],
  resolve: {
    alias: {
      '~': path.resolve(__dirname, './src'),
      '@': path.resolve(__dirname, './src')
    }
  },
  test: {
    globals: true,
    environment: 'happy-dom'
  }
})

※本手順書ではソースディレクトリを「src」に変更している想定としています。

// nuxt.config.ts
export default defineNuxtConfig({
  srcDir: 'src/'
});


3. 「package.json」に以下を追加します。

{
  "config": {
    "path": "./src/unitTest/index.spec.ts"
  },
  "scripts": {
    "test:all": "vitest",
    "test:linux": "vitest $npm_package_config_path",
    "test:win": "vitest %npm_package_config_path%"
  },
}


4. テストを全件(全テストファイル)実施するには以下のコマンドを実行します。

$ npm run test:all


5. 任意のテストを実施するには以下のコマンドを実行します。予め「package.json」のconfigのpathに実行したい任意のテストファイルを指定しておきます。

Windowsの場合 $ npm run test:win
Linux/Macの場合   $ npm run test:linux


6. 「src」ディレクトリ内に「unitTest」ディレクトリを作成します。

$ cd src
$ mkdir unitTest


7. 「src」配下の「unitTest」ディレクトリ内に「index.spec.ts」を作成して以下の内容で保存します。

import { describe, expect, test } from 'vitest'
import { render, screen } from '@testing-library/vue'
import Index from '~/pages/index.vue'

describe('Index', () => {
  test('Indexページにタイトルが表示されていること', () => {
    // Arrange
    render(Index)

    // Assert
    expect(screen.getByText('Pages/index.vue')).toBeTruthy()
  })
})


8. 「src」配下の「pages」ディレクトリ内に「index.vue」を作成して以下の内容で保存します。

<template>
  <h1>
    Pages/index.vue
  </h1>
</template>


9. テストを実施します。テストが成功することを確認します。

$ npm run test:all
> test:all
> vitest
 ✓ src/unitTest/index.spec.ts (1)
   ✓ Index (1)
     ✓ Indexページにタイトルが表示されていること
 Test Files  1 passed (1)
      Tests  1 passed (1)


自動インポート設定


1. 「src」配下に「components」ディレクトリを新規作成します。「components」ディレクトリに、「Button.vue」を新規作成して以下の内容で保存します。

<template>
  <button>ボタン</button>
</template>


2. 前項で作成した「src」配下の「pages」ディレクトリ内に存在する「index.vue」を以下の内容で修正して保存します。

<script lang="ts" setup>
// Nuxtが自動インポートするref関数を追加
const isClicked = ref(false)
const handleClick = () => {
  isClicked.value = true
}
</script>
<template>
  <!-- Nuxtが自動インポートするコンポーネントを追加 -->
  <Button @click="handleClick" />
  <p v-if="isClicked">
    Clicked!
  </p>
</template>


3. 前項で作成した「src」配下の「unitTest」ディレクトリ内にに存在する「index.spec.ts」を以下の内容で修正して保存します。

import { describe, expect, test } from 'vitest'
import { render, screen } from '@testing-library/vue'
import userEvent from '@testing-library/user-event'
import Index from '~/pages/index.vue'

describe('Index', () => {
  test('ボタンをクリックすると画面に文言が表示されていること', async () => {
    // Arrange
    const user = userEvent.setup()
    render(Index)

    // Act
    await user.click(screen.getByText('ボタン'))

    // Assert
    expect(screen.getByText('Clicked!')).toBeTruthy()
  })
})


4. テストを実施します。テストが失敗することを確認します。

$ npm run test:all
> test:all
> vitest
stderr | src/unitTest/index.spec.ts > Index > Indexページにタイトルが表示されていること
[Vue warn]: Failed to resolve component: Button
If this is a native custom element, make sure to exclude it from component resolution via compilerOptions.isCustomElement. 
⎯⎯⎯⎯⎯ Failed Tests 1 ⎯⎯⎯⎯
 FAIL  src/unitTest/index.spec.ts > Index > Indexページにタイトルが表示されていること
ReferenceError: ref is not defined
 ❯ setup src/pages/index.vue:2:19
      1| <script lang="ts" setup>
      2| const isClicked = ref(false)
       |                   ^


5. ライブラリをインストールします。

$ npm install --save-dev unplugin-auto-import unplugin-vue-components


6. 「vitest.config.ts」のpluginsにAutoImportFunctionsとAutoImportComponentsを追加します。AutoImportFunctionsのimportsにはインポートしたいライブラリを記述します。プリセットが用意されているライブラリに関しては以下のように直接ライブラリ名を指定できます。

// import文を追加
import AutoImportFunctions from 'unplugin-auto-import/vite'
import AutoImportComponents from 'unplugin-vue-components/vite'

export default defineConfig({
  plugins: [
    Vue(),
     // 以下を追加
    AutoImportFunctions (
      {
        // インポートするライブラリにはプリセットが用意されています
        // https://github.com/antfu/unplugin-auto-import/tree/main/src/presets
        imports: [
          'vue',
          'vee-validate',
          'vue-router',
          'pinia',
        ],
        dts: 'auto-imports.d.ts'
      }
    ),
    AutoImportComponents({
      // ソースディレクトリをsrcに変更している想定なのでここではsrc/componentsとしています
      dirs: ['src/components'],
      dts: '.nuxt/components.d.ts',
    }),
  ],
})

※プリセットが用意されていないライブラリをインポートするには以下のように記載します。

export default defineConfig({
  plugins: [
    Vue(),
    AutoImportFunctions ({
      imports: [
        {
          '@vueuse/core': [
            'useMouse', // = import { useMouse } from '@vueuse/core',
          ],
        }
      ]
    })
  ],
})


7. テストを実施します。テストが成功することを確認します。

$ npm run test:all
 ✓ src/unitTest/index.spec.ts (1)
   ✓ Index (1)
     ✓ ボタンをクリックすると画面に文言が表示されていること
 Test Files  1 passed (1)
      Tests  1 passed (1)


カバレッジの取得

1. カバレッジの取得に必要なパッケージをインストールします。

$ npm install --save-dev @vitest/coverage-v8


2. 「vitest.config.ts」のtestに以下を追加して保存します。

export default defineConfig({
  test: {
    // 以下を追加
    coverage: {
      provider: 'v8',
      include: ['src/**/*.{vue,js,ts}'],    // src配下の特定の拡張子のファイルのみをテスト対象に設定
      all: true,                            // 未テストのコードもカバレッジの対象にする
      reporter: ['html', 'clover', 'text']
    },
    root: '.',
    reporters: ['verbose'],
  }
});


3. 「package.json」のscriptsの「test:all/test:linux/test:win」を以下の内容で修正して保存します。

{
  "scripts": {
    "test:all": "vitest --coverage",
    "test:linux": "vitest --coverage $npm_package_config_path",
    "test:win": "vitest --coverage %npm_package_config_path%"
  },
}


4. テストを実施します。テストが成功してカバレッジが表示されていることを確認します。

$ npm run test:all
> test:all
> vitest --coverage
 ✓ src/unitTest/index.spec.ts (1)
   ✓ Index (1)
     ✓ ボタンをクリックすると画面に文言が表示されていること
 Test Files  1 passed (1)
      Tests  1 passed (1)
 % Coverage report from v8
-------------|---------|----------|---------|---------|-------------------
File         | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
-------------|---------|----------|---------|---------|-------------------
All files    |     100 |      100 |     100 |     100 | 
 components  |     100 |      100 |     100 |     100 | 
  Button.vue |     100 |      100 |     100 |     100 | 
 pages       |     100 |      100 |     100 |     100 | 
  index.vue  |     100 |      100 |     100 |     100 | 
-------------|---------|----------|---------|---------|-------------------



以上で全ての手順は完了になります