⚡️

Nest.js × Vitest の環境構築

2024/02/12に公開

はじめに

Nest.js で Vitest を用いてテストの環境構築をしたので、その経緯と手順をまとめます。

Vitest を採用した理由

  • HMR のように、変更があったモジュールだけを再実行することができ、実行速度が速いため
  • esbuild によって ESM, TS が標準サポートされているため
  • チームメンバーが推奨してくれたため
  • 自分自身が参画していたプロジェクトで Jest を使用していましたが、テストの実行速度が気になっていたため

Vitest を使う上で懸念に思っていたこと

Jest と同じような API を提供していくれているのかが気になっていました。
調べたところ、公式にも以下のようなことが書かれており、Jest との互換性があるとのことでした。

Given Jest's massive adoption, Vitest provides a compatible API that allows you to use it as a drop-in replacement in most projects.

(DeepL 翻訳)
Jestが大量に採用されていることから、Vitestは互換性のあるAPIを提供しており、ほとんどのプロジェクトでJestをドロップインで置き換えることができます。

https://vitest.dev/guide/why.html#the-need-for-a-vite-native-test-runner

また、API Reference も見てみたところ、Jest と同じような API を提供していることがわかりました。
describe, it, expect などの API が提供されています。
また、beforeEach, afterEach などの Setup, Teardown 関数も用意されています。

https://vitest.dev/api/#test-api-reference

モック関数についても、vi ヘルパーを使ってモック関数を作成することができます。

https://vitest.dev/api/vi.html

環境構築

前提

以下のバージョンで Nest.js プロジェクト作成されていること

※ちなみに筆者は以下のバージョンでNestプロジェクトを作成しました。

パッケージ バージョン
Node.js 20.9.0
npm 10.1.0
Nest.js 10.2.9

手順

1. Jest のアンインストールと設定ファイル・記述の削除

jest.config.ts
- import type { Config } from 'jest'
-
- const config: Config = {
-   moduleFileExtensions: ['js', 'json', 'ts'],
-   rootDir: 'src',
-   testRegex: '.*\\.spec\\.ts$',
-   transform: {
-     '^.+\\.(t|j)s$': 'ts-jest',
-   },
-   collectCoverageFrom: ['**/*.(t|j)s'],
-   coverageDirectory: '../coverage',
-   testEnvironment: 'node',
- }
-
- export default config
test/jest-e2e.json
- {
-   "moduleFileExtensions": ["js", "json", "ts"],
-   "rootDir": ".",
-   "testEnvironment": "node",
-   "testRegex": ".e2e-spec.ts$",
-   "transform": {
-     "^.+\\.(t|j)s$": "ts-jest"
-   }
- }
terminal
$ npm un -D jest @types/jest ts-jest

2. Vitest のインストールと設定ファイルの作成・記述

基本的に Nest 公式の Vitest セクションの手順に従います。

https://docs.nestjs.com/recipes/swc#vitest

terminal
$ npm i --save-dev vitest unplugin-swc @swc/core @vitest/coverage-v8
vitest.config.mts
+ import swc from 'unplugin-swc'
+ import { defineConfig } from 'vitest/config'
+
+ export default defineConfig({
+   test: {
+     globals: true,
+     root: './',
+   },
+   plugins: [
+     swc.vite({
+       module: { type: 'es6' },
+     }),
+   ],
+ })
vitest.config.e2e.mts
+ import swc from 'unplugin-swc'
+ import { defineConfig } from 'vitest/config'
+
+ export default defineConfig({
+   test: {
+     include: ['**/*.e2e-spec.ts'],
+     globals: true,
+     root: './',
+   },
+   plugins: [swc.vite()],
+ })

ESM の記述になるため、.mts という拡張子にしています。
または、package.json の type フィールドに module を追加することで、.ts という拡張子でも記述できます。

package.json
+ {
+   "type": "module",
+ }

3. テストスクリプトの設定

package.json
  "scripts": {
-     "test": "jest",
-     "test:watch": "jest --watch",
-     "test:cov": "jest --coverage",
-     "test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
-     "test:e2e": "jest --config ./test/jest-e2e.json"
+     "test": "vitest run",
+     "test:watch": "vitest",
+     "test:cov": "vitest run --coverage",
+     "test:debug": "vitest --inspect-brk --inspect --logHeapUsage --poolOptions.threads.singleThread",
+     "test:e2e": "vitest run --config ./vitest.config.e2e.mts"
  },

4. ダミーのテストファイルにテストを記述

src/auth/auth.controller.spec.ts
import { Test, type TestingModule } from '@nestjs/testing'
import { beforeEach, describe, expect, it } from 'vitest'

import { AuthController } from './auth.controller'

describe('AuthController', () => {
  let controller: AuthController

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      controllers: [AuthController],
    }).compile()

    controller = module.get<AuthController>(AuthController)
  })

  it('Controller が定義されていること', () => {
    expect(controller).toBeDefined()
  })
})

5. テストの実行

terminal
$ npm run test

テストの実行結果

おまけの設定

  • VSCode 拡張機能のインストール

https://marketplace.visualstudio.com/items?itemName=ZixuanChen.vitest-explorer

VSCode 上でテストの実行と、テストファイル内でテストの結果を表示できるようになります。

vscode上のテスト実行と表示

チームメンバーにもおすすめしておきましょう。

.vscode/extensions.json
{
  "recommendations": [
    // ...
+   "zixuanchen.vitest-explorer"
  ]
}
  • CI でのテストの実行
.github/workflows/ci.yml
+   test:
+     runs-on: ubuntu-latest
+     steps:
+       - name: checkout
+         uses: actions/checkout@v4
+       - name: setup node
+         uses: actions/setup-node@v4
+         with:
+           node-version-file: './package.json'
+       - name: clean install
+         run: npm ci
+       - name: test
+         run: npm run test
+
+   test-e2e:
+     runs-on: ubuntu-latest
+     steps:
+       - name: checkout
+         uses: actions/checkout@v4
+       - name: setup node
+         uses: actions/setup-node@v4
+         with:
+           node-version-file: './package.json'
+       - name: clean install
+         run: npm ci
+       - name: test
+         run: npm run test:e2e

ベンチマーク

Jest と Vitest の実行速度を比較してみました。
1つのテストだけで約2.9倍、約1秒の速度差がありました。
驚きです。

Jest
1.386s

Jestベンチマーク

Vitest
477ms

Vitestベンチマーク

おわりに

Vitest を採用してみて、テストの実行速度が速くなることを実感しました。
Jest との互換性もあるため、移行もスムーズにできると思うので、ぜひ試してみてください。

GitHubで編集を提案
OT Official

Discussion