Vitestは速いのか Jestと比較を行ってみる
TL;DR
- Vitestを使えば(Viteを使っていると)一つの設定ファイルでテストまでサポートできる
- CI/CDでの実行は、swc/jestに比べると遅くなる可能性があり
- 手元でのwatchの実行に関しては最速
Vitestって何?
Vitestについて説明する前にまず、Viteについて説明します。
Vite
概要
Viteはここ2年ほどで登場したビルドツールです。 vから始まるのでもしや、という方もいると思いますが、Vue.jsの開発者Evan You氏などによって開発されています。
Vueを作った人が作ってるんだからVueじゃないとダメなんじゃないの?
と思われるかもしれませんが、特定のフレームワークやライブラリをサポートしているわけではなく、
- React
- Preact
- Svelte
などでも利用可能です。(Svelteに関しては公式のtemplateで使われてました)
Viteの利点
ここでViteの利点を説明するためにwebpackと対比させます。(webpackを否定するつもりはないのでご容赦ください)
昨今のフロントエンド開発では、ES6やTypeScriptを一旦トランスパイルし、それをbundle化して一つのjsファイルにしていました。
この場合次のような問題点があります。
- 初回のリクエストまでに時間がかかる(ビルドをしないといけないため)
- 更新の際にビルドして再度bundle化が必要
では、Viteはどのようなスタンスでこれらの問題を解決しようとしているかというと、Native ESM (ES Module)
です。
今まで、ES Moduleはブラウザで使うことができず、import
などの依存の解決をビルド時に行い、bundle化しなければいけませんでした。
しかし、ブラウザがES Moduleをサポートしたことで次のことが可能となります。
- bundle化をせずとも依存問題を解決できる
- 必要になるまでモジュールの読み込みや評価をしない
これによりBundle化などの余計なオーバーヘッドを減らし高速化を図っています。
Vitest
Viteの説明をしたところでVitestの説明をしていきます。
Vitestは、Viteでお馴染みのAnthony Fu氏らによって作られたテストフレームワークです。 名前からもわかる通り、Viteを活用したものとなっており、次のような特徴があります。
- ViteのConfigをそのまま活用できる
- transformにViteのDev Serverを活用している
- Jestとほぼ同じようなAPIを提供している
ViteのConfigをそのまま活用できる
Vitestは、ViteのConfigを流用でき、一つのパイプラインで開発からビルド、テストが可能となります。 今までだとwebpackにjestのconfigに...と複数のconfigを運用する必要がありましたが、その煩わしさから解放されます。 (もちろん、別でconfigを追加することも可能です)
transformにViteのDev Serverを活用している
これにより、前述のVite configができるだけでなく、ViteのもつHMR(Hot Module Replacement)が有効活用できます。
Jestとほぼ同じようなAPIを提供している
便利だとはいえ、APIなどが変わってしまうとなかなか移行が大変ですが、VitestはJestとほぼ同じようなAPIで、 以降に関しては容易に行えます。
Why Vitestに詳しく書かれています。
Vitestでテストを書いてみる
GitHubに挙げています。
テストに関してはJestとほぼ同じように書けます。
import { describe, expect, it, vi } from 'vitest';
import { fireEvent, render, screen } from '@testing-library/react';
import React from 'react';
import { Input } from '../../src/components/Input';
describe('components/Input', () => {
it('correctly render', () => {
const component = render(<Input value={'sample'} />);
expect(component.container).toMatchSnapshot();
});
it('correctly fire event', async () => {
const testId = 'unit';
const onChangeMock = vi.fn();
render(<Input testId={testId} value={'dddd'} onChange={onChangeMock} type="text" />);
const input = await screen.getByTestId(`${testId}-input`);
fireEvent.change(input, {
target: {
value: 'test',
},
});
expect(onChangeMock).toBeCalled();
});
});
Jestの経験があれば特に書くのに困らない思います。
パフォーマンス評価
Viteが速いと知られているので、Vitestも速いのでは、と思い検証してみました。
次のJestの構成でスピードの比較をして見ました。
ts-jest
swc/jest
パフォーマンスを評価するにあたって次のようなパターンを用意しました。
- コンポーネントの依存関係が浅く、同じ階層に大量のコンポーネントがある場合(Concurrent)
- 次のようにコンポーネントが同じ階層に配置されているアプリケーション
<Component1/>
<Component2/>
.
.
.
<Component200/>
- コンポーネントの依存関係が深い場合 (以下、Serial)
- 次のようにコンポーネントが入れ子になっているような構成のアプリケーション
<Component1>
<Component2>
.
.
.
<Component200 />
</Component2>
</Component1>
なお、基本的に断りのない限り、テストの実行はwatchを行わないsingle runを5回実行した際の平均で比較を行ってます。
Concurrentのテスト結果
ts-jest | swc/jest | Vitest | |
---|---|---|---|
実行時間[sec] | 5.96 | 3.83 | 12.19 |
上記のようにswc/jest
が一番早く、Vitestはts-jest
の倍近くの時間がかかっているようです。
今回のケースの場合、モジュールがそれぞれ独立しており、あまりVite serverによるHMRの恩恵が受けられないことも理由にありますが、このようなテスト実行は向かないかもしれないですね。
Serialのテスト結果
ts-jest | swc/jest | Vitest | |
---|---|---|---|
実行時間[sec] | 8.36 | 6.32 | 14.02 |
依存関係のチェーンがあるせいか全てのケースで遅くなってます。 これだけみるとVitestの性能悪化が思ったほど大きくないですね。 とはいえ、Vitestはやはり遅いようです。
Vitestは遅いのか、とちょっと気になったので、今度はwatchモードで、一番下にあるコンポーネントを修正した際に走るテストの実行速度を比較しました。
ts-jest | swc/jest | Vitest | |
---|---|---|---|
実行時間[sec] | 10.22 | 8.39 | 7.26 |
この場合、jest系統は少し性能が悪化しているにもかかわらず、Vitestの方はむしろ高速化してます。 ここら辺は、ViteのもつES ModuleベースのHMRがうまく効いているかもしれません。
実際のアプリケーションでの比較
今まではベンチマークのためにあまり意味のないアプリケーション、コンポーネントで検証して見ました。 ただ、実際のアプリケーションだとどうなるかはそれだとわからないので簡単なTodoアプリで検証しました。
今回、検証に使ったアプリはこちらです。
Single run
まずはsingle runでの比較を行いました。
ts-jest | swc/jest | Vitest | |
---|---|---|---|
実行時間[sec] | 2.292 | 1.110 | 1.238 |
先ほどのアプリケーションとは違って、ts-jest
とよりは速くなってます。
とはいえ、swc/jest
に若干ではありますが負けてます。
ただ、0.1秒ほどなので、そこまで気にはならないと言えば気になりません。
Watchモードでの実行
次に、watchモードでファイルの監視を行い、1ファイルだけ変更した場合の比較を行いました。
ts-jest | swc/jest | Vitest | |
---|---|---|---|
実行時間[sec] | 2.91 | 0.71 | 0.22 |
この場合だと、ViteのHMRがうまく効いているからか、Vitest
が一番速くなってます。
Vitestの「速い」はこのことを指してるかもしれないですね。
まとめ
Vitestとjestを比較して見ましたが、Single runではjestをチューニングして使う方が速いものの、 watchモードで高速に動くことがわかります。
まだまだVitest自体は開発中ですが、
- Viteを既に導入している場合
- CIでのテスト時間がそこまでかからない場合
- watchモードなどでテストを手元で実行している場合
は、今後テストツールとして選択肢に上がってくるかもしれません。