⚡️

Next.js×TypeScript環境でJest→Vitestに移行してみた

2023/07/21に公開

はじめに

普通に新たな Vite プロジェクトを作って、Vitest を始める場合は先日公開したこちらの記事をご覧ください。

リンク入れるリンク入れるリンク入れるリンク入れるリンク入れるリンク入れる

今回はタイトルにもあるように、移行がテーマです。

以前自分が作成した Next.js×TypeScript のプロジェクトで Jest を使っており、それを Vitest へ移行する対応を行なった時のまとめがこの記事の内容です。

実際の対応内容はこちらのプルリクをご覧いただければよく分かるかと思います。

https://github.com/ysknsid25/booby/pull/21/files

やったこと

だいたいこんな感じです。

  1. Vitest のインストール
  2. vitest.config.tsを用意する
  3. npx vitest runして出てきたエラーを潰す

では詳しく書きます。

Vitest のインストール

npm install するだけ。

npm install -D vitest

あと、Vitest の実行のために vite の React プラグインが必要ですので、インストールしておきます。

npm install -D @vitejs/plugin-react

vitest.config.tsを用意する

vitest の設定のためにvitest.config.tsを用意して、設定を書いていきます。
このあとのエラーを解消するためにいろいろ書き足した最終系を載せておきます。

vitest.config.ts
/// <reference types="vitest" />
import react from '@vitejs/plugin-react'
import { defineConfig } from 'vitest/config'

export default defineConfig({
  plugins: [react()],
  test: {
    globals: true,
    environment: 'jsdom',
    setupFiles: './src/test/setup.ts',
  },
  resolve: {
    alias: {
      '@': __dirname + '/src',
    },
  },
})

初期状態は Vitest がサンプルを公開してくれているので、そちらを見るとよいでしょう。

https://github.com/vitest-dev/vitest/blob/main/examples/nextjs/vitest.config.ts

src/test/setup.ts を作る

これは必須対応というわけではないのですが、全てのテストファイルに@testing-library/jest-domの import 文を書く手間を省くために入れたという感じです。
その他必要な前処理があれば、setup.tsに書くという感じです。
今回は import 文だけです。

setup.ts
import '@testing-library/jest-dom'

npx vitest runして出てきたエラーを潰す

まずは実行前に、全てのテストコードにvitestの import を書き足します。

testdescribeexpectは Jest のものなので、この辺りを import してあげる必要があります。

import { expect, test, describe } from 'vitest'

これだけ入れたら、一旦npx vitest runしてみます。すると案の定エラーが出ます。

出てきたエラー

ざっとこんなな感じです。

 FAIL  src/Components/__test__/header.test.jsx [ src/Components/__test__/header.test.jsx ]
ReferenceError: describe is not defined
 ❯ src/Components/__test__/header.test.jsx:5:1
      3| import Header from '../header'
      4|
      5| describe('Header部分のテスト', () => {
       | ^
      6|   test('boobyというタイトル文字が表示されているかどうか', () => {
      7|     const { getByText } = render(<Header />)

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[1/5]⎯

 FAIL  src/Components/__test__/pagination.test.tsx [ src/Components/__test__/pagination.test.tsx ]
ReferenceError: jest is not defined
 ❯ src/Components/__test__/pagination.test.tsx:5:1
      3| import Pagination from '../pagination'
      4|
      5| jest.mock('next/router', () => ({
       | ^
      6|   useRouter: jest.fn().mockReturnValue({
      7|     pathname: '/',

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[2/5]⎯

 FAIL  src/Components/__test__/repositoryCard.test.jsx [ src/Components/__test__/repositoryCard.test.jsx ]
Error: Failed to load url @/Components/repositoryCard (resolved id: @/Components/repositoryCard) in /rootpath/src/Components/__test__/repositoryCard.test.jsx. Does the file exist?
 ❯ loadAndTransform node_modules/vite/dist/node/chunks/dep-8609dc5d.js:54816:21

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[3/5]⎯

 FAIL  src/Components/__test__/repositorySearchForm.test.jsx [ src/Components/__test__/repositorySearchForm.test.jsx ]
Error: Failed to load url @/Components/repositorySearchForm (resolved id: @/Components/repositorySearchForm) in /rootpath/src/Components/__test__/repositorySearchForm.test.jsx. Does the file exist?
 ❯ loadAndTransform node_modules/vite/dist/node/chunks/dep-8609dc5d.js:54816:21

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/5]⎯

 FAIL  src/Components/__test__/select.test.jsx [ src/Components/__test__/select.test.jsx ]
Error: Failed to load url @/Components/select (resolved id: @/Components/select) in /rootpath/src/Components/__test__/select.test.jsx. Does the file exist?
 ❯ loadAndTransform node_modules/vite/dist/node/chunks/dep-8609dc5d.js:54816:21

⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[5/5]⎯

 Test Files  5 failed (5)
      Tests  no tests
   Start at  07:57:15
   Duration  903ms (transform 230ms, setup 0ms, collect 0ms, tests 0ms, environment 805ms, prepare 180ms)

分類するとこんな感じです。

  • ReferenceError
    • describe is not defined
    • jest is not defined
  • Error: Failed to load url

対処法を書いていきます。

ReferenceError

これは単純に Jest から Vitest に置き換えたことで存在しない export を呼んでいることによるエラーです。

describe は単に先に書いた import 漏れなのですが、もう一つは Jest の mock を使っていることによるエラーでした。

修正前
jest.mock('next/router', () => ({
  useRouter: jest.fn().mockReturnValue({
    pathname: '/',
    query: { language: 'javascript' },
  }),

では、Jest の mock を Vitest の mock へ置き換える必要がありますが、どうしたかというとこうしました。

修正後
vi.mock('next/router', () => ({
  useRouter: vi.fn().mockReturnValue({
    pathname: '/',
    query: { language: 'javascript' },
  }),

はい、単にjestからviに置き換えただけです。

一応後日また Vitest の mock をいろいろ試した記事を公開する予定です。

Error: Failed to load url

これは import で@/Components/selectのようにパスエイリアスを使っていることが問題になってました。

要は、Vitest は@がどこのパスを指しているのか、そのままだと解釈できていないということです。

なのでvitest.config.tsdefineConfigに以下の設定を追加する必要がありました。

vitest.config.ts
resolve: {
    alias: {
        '@': __dirname + '/src',
    },
},

これで vitest は@/Components/selectをうまく認識してくれるようになりました。

おわりに:対応してみた感想

意外と簡単に対応できました。

Vitest 公式に Jest からの移行のドキュメントがあるくらいなので、だいぶ意識して作ってるっぽいです。

https://vitest.dev/guide/migration.html

ただ、mock とか snapshot がいっぱいあるときにどうなるまでは見切れてはない感じなので、それ次第でもしかすると難易度は多少変わってくるのかもしれません。

メンバー募集!

サーバーサイド Kotlin コミュニティを作りました!

Kotlin ユーザーはぜひご参加ください!!

https://serverside-kt.connpass.com/

また関西在住のソフトウェア開発者を中心に、関西エンジニアコミュニティを一緒に盛り上げてくださる方を募集しています。

よろしければ Conpass からメンバー登録よろしくお願いいたします。

https://blessingsoftware.connpass.com/

Discussion