jiko21’s techblog

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ファイルにしていました。

webpack

この場合次のような問題点があります。

  • 初回のリクエストまでに時間がかかる(ビルドをしないといけないため)
  • 更新の際にビルドして再度bundle化が必要

では、Viteはどのようなスタンスでこれらの問題を解決しようとしているかというと、Native ESM (ES Module)です。

今まで、ES Moduleはブラウザで使うことができず、importなどの依存の解決をビルド時に行い、bundle化しなければいけませんでした。 しかし、ブラウザがES Moduleをサポートしたことで次のことが可能となります。

  • bundle化をせずとも依存問題を解決できる
  • 必要になるまでモジュールの読み込みや評価をしない

vite

これにより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のテスト結果

single runでの比較

ts-jestswc/jestVitest
実行時間[sec]5.963.8312.19

上記のようにswc/jestが一番早く、Vitestはts-jestの倍近くの時間がかかっているようです。

今回のケースの場合、モジュールがそれぞれ独立しており、あまりVite serverによるHMRの恩恵が受けられないことも理由にありますが、このようなテスト実行は向かないかもしれないですね。

Serialのテスト結果

single runでの比較

ts-jestswc/jestVitest
実行時間[sec]8.366.3214.02

依存関係のチェーンがあるせいか全てのケースで遅くなってます。 これだけみるとVitestの性能悪化が思ったほど大きくないですね。 とはいえ、Vitestはやはり遅いようです。

Vitestは遅いのか、とちょっと気になったので、今度はwatchモードで、一番下にあるコンポーネントを修正した際に走るテストの実行速度を比較しました。 watchでの比較

ts-jestswc/jestVitest
実行時間[sec]10.228.397.26

この場合、jest系統は少し性能が悪化しているにもかかわらず、Vitestの方はむしろ高速化してます。 ここら辺は、ViteのもつES ModuleベースのHMRがうまく効いているかもしれません。

実際のアプリケーションでの比較

今まではベンチマークのためにあまり意味のないアプリケーション、コンポーネントで検証して見ました。 ただ、実際のアプリケーションだとどうなるかはそれだとわからないので簡単なTodoアプリで検証しました。

今回、検証に使ったアプリはこちらです。

Single run

まずはsingle runでの比較を行いました。

single runでの比較

ts-jestswc/jestVitest
実行時間[sec]2.2921.1101.238

先ほどのアプリケーションとは違って、ts-jestとよりは速くなってます。 とはいえ、swc/jestに若干ではありますが負けてます。 ただ、0.1秒ほどなので、そこまで気にはならないと言えば気になりません。

Watchモードでの実行

次に、watchモードでファイルの監視を行い、1ファイルだけ変更した場合の比較を行いました。

watchでの比較

ts-jestswc/jestVitest
実行時間[sec]2.910.710.22

この場合だと、ViteのHMRがうまく効いているからか、Vitestが一番速くなってます。 Vitestの「速い」はこのことを指してるかもしれないですね。

まとめ

Vitestとjestを比較して見ましたが、Single runではjestをチューニングして使う方が速いものの、 watchモードで高速に動くことがわかります。

まだまだVitest自体は開発中ですが、

  • Viteを既に導入している場合
  • CIでのテスト時間がそこまでかからない場合
  • watchモードなどでテストを手元で実行している場合

は、今後テストツールとして選択肢に上がってくるかもしれません。

参考

about author...

Frontend engineer.
loves: TypeScript, React, Node.js

more detail...