ちょっと話題の記事

あたらしいテストフレームワークVitestをReactで試してみた

Jestに代わるVite環境で動作するあたらしい高速テストフレームワークVitestをReactで試してみました。
2022.01.19

はじめに

こんにちは、CX事業本部MAD事業部の森茂です。

今回まだ開発版ではありますが、昨年末に公開されたVue.jsやViteの作者Anthony Fu氏@antfu7、Patak氏@patak_devが開発しているJestに代わるあたらしい高速テストフレームワークVitestをReactで試してみました。

VitestはVite環境で動作する高速なテストフレームワークで、VueやReact、Svelte、litなどなどさまざまなフレームワークで動作します。ChaiやJestのアサーション、スナップショットが互換的に動作するので既存のテストコードをほぼ同じ書き方で利用することができます。またc8を利用したcoverageの作成にも対応しています。

Jestでは環境の構築にそれなりの手間がかかりますが、Vitestはライブラリのインストールと簡単な設定を追記するだけで利用することが可能です。

なお、Vitestはまだ開発中のテストフレームワークのためプロダクションでの利用は推奨されていませんのでその点ご了承ください。

Vite環境の構築

今回はcreate viteを利用してReact + TypeScriptの環境を用意します。

$ yarn create vite vitest-react-sample --template react-ts
$ cd vitest-react-sample

create vite直後は下記のディレクトリ構成となります。

├── index.html
├── package.json
├── src
│   ├── App.css
│   ├── App.tsx
│   ├── favicon.svg
│   ├── index.css
│   ├── logo.svg
│   ├── main.tsx
│   └── vite-env.d.ts
├── tsconfig.json
├── vite.config.ts
└── yarn.lock

この状態で一度起動の確認をしておきます。

$ yarn dev

今回は初期時に用意されているこの画面に対してVitestで簡単なテストコードを用意していきます。

Vitestのセットアップ

Vitestをインストールします。(執筆時の最新版はv0.1.20)

$ yarn add -D vitest

React TestingライブラリなどReactのテストに必要な各種ライブラリをインストールします。また今回DOMのレンダリングに利用するのはjs-domではなくjs-dom互換でSSRにも対応、パフォーマンスも倍以上速いと謳っているhappy-domを利用してみました。

$ yarn add -D @testing-library/react
$ yarn add -D @testing-library/jest-dom
$ yarn add -D happy-dom
$ yarn add -D @testing-library/user-event @types/testing-library__user-event
$ yarn add -D jest-extended

Vitest用の設定をvite.config.tsファイルに追記します。設定を一部追記するだけでテスト環境が用意できるのは楽ですね。

vite.config.ts

/// <reference types="vitest" />

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vitejs.dev/config/
export default defineConfig({
  plugins: [react()],
  test: {
    global: true,
    environment: 'happy-dom',
    setupFiles: './src/setup.ts',
  },
})

test実行用のセットアップファイルを用意します。jest用の拡張されたアサーションも利用できるようにimportしておきます。もちろん利用する必要がない場合は不要です。

src/setup.ts

import '@testing-library/jest-dom/extend-expect'
import 'jest-extended';

App.test.tsxファイルとしてテストコードを用意します。スナップショットとレンダリング結果に対してのテキスト要素のチェックを行う単純なテストコードです(extend-expectjest-extendedのアサーションを使うために少々無理やり感ありです)。describetestなどJestとは異なりグローバルでは展開されないため、それぞれをimportする必要がありますが、記載方法はほぼ同じにとなっているので既存のテストコードからの移行もしやすいと思います。

src/App.test.tsx

import '@testing-library/jest-dom' // jestのアサーションがエラーになる場合は明示的にimport
import { describe, expect, test } from 'vitest'
import App from './App'
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'

describe('Simple working test', () => {

  test('should render correctly', () => {
    const {container} = render(<App />)
    expect(container.firstChild).toMatchSnapshot()
  })

  // extend-expectのmatcher[toBeInTheDocument]
  test('the title is visible', () => {
    render(<App />)
    expect(screen.getByText(/Hello Vite \+ React!/i)).toBeInTheDocument()
  })

  // jest-extendedのmatcher[toBeEmptyDOMElement]
  test('should increment count on click', async() => {
    render(<App />)
    userEvent.click(screen.getByRole('button'))
    expect(await screen.findByText(/count is: 1/i)).not.toBeEmptyDOMElement()
  })
})

なお、今回検証のために用意した環境ではJestの拡張されたアサーションの型情報がVitestにはまだ入っていないようでエディタ上ではTypeエラーが出ました。Vite環境ではtype-checkが走らないのでこのままでも動作はしますが、現時点では自分で型情報を用意するなど別途対応が必要そうです。(明示的にimportすることでもTypeエラーは消えるようですが何か設定が足りないだけなのかもしれません。。。)

テストの実行

環境の準備ができたところでさっそくテストを実行してみます。

$ yarn test

初回スナップショットの生成も合わせてこの1.15秒で完了です。

一部テストコードを書き換えて保存した際の再テストについては20msと一瞬で終わりました:)

coverageをとってもこの速度。

Jestとの比較

参考までに同じプロジェクトにJest(ts-jest)を導入して何度が計測してみたところ結果は下記のようになりました。

テスト内容 Vitest Jest
App.test.tsx 1.15s 3.77s
watch 20ms 3.181s
coverage 1.77s 6.024s

単純なテストですがそれでも各項目で想像以上に速度の差があることがわかりました。

開発環境

  • MacBook Pro (13-inch, M1, 2020)
  • macOS Monterey
  • node v16.13.1
  • vite v2.7.2
  • vitest v0.1.20
  • jest 27.4.7
  • ts-jest 27.1.3
  • happy-dom 2.27.0

さいごに

まだ開発中ながら一度この速度感に慣れてしまうとJestには戻れなくなってしまいそうです。。Jest互換の記載で書きすすめることができ、移行も容易そうなので正式リリース後にはテストフレームワークとして有力な選択肢となりそうです。Vitest今後の開発が楽しみですね。