Storybookインタラクションテストについて#
問題提起#
Storybookはバージョン9のメジャーリリースと同時に、@storybook/addon-vitestの正式版をリリースしました。
私がメンテナンスしているプロジェクトでは、すでに@testing-library/reactを使用した単体テストが多数存在しますが、Storybookのインタラクションテストはまだ導入されていません。
では、このインタラクションテストと、筆者がよく知る単体テストやE2E(エンドツーエンド)テストとの違いは何でしょうか?
どのようなテストをインタラクションテストで書くべきで、どのようなテストを単体テストで書くべきなのでしょうか?(E2Eテストの対象は明確で、完全な環境でのユーザー操作をテストするものです)
事前調査#
Storybookの公式ドキュメントでは、最大の違いとして次の一つだけが挙げられています。
インタラクションテストはブラウザ上でテストの実行を確認でき、デバッグが容易である。
JSDOMのエラー修正を何度も経験したことがある方なら、コンソールに出力された大量のDOMを前に途方に暮れた経験があるでしょう。筆者はまだStorybookでのデバッグを実際に試したことはありませんが、この点においてStorybookのインタラクションテストがJSDOMに対して大きな利点を持つことは間違いありません。
では、他に違いはあるのでしょうか?
Geminiによる回答#
各種テストの比較#
| 特徴 | コンポーネントテスト | 単体テスト | E2Eテスト |
|---|---|---|---|
| スコープ | 単一コンポーネント | 単一関数/モジュール | アプリケーション全体のフロー |
| 焦点 | ビジュアル、インタラクション、統合 | ビジネスロジック、アルゴリズム | 完全なユーザージャーニー |
| 環境 | ブラウザ(分離) | Node.js | 実際のブラウザ(完全なアプリケーション) |
| 速度 | やや速い | 最速 | 最も遅い |
| 主なツール | Storybook, Playwright, Testing Library | Jest, Vitest | Playwright, Cypress |
Storybookテストとtesting-libraryの比較#
| 特徴 | testing-library (Jest/Vitestと併用) | Storybookテスト (@storybook/addon-vitest) |
|---|---|---|
| 実行環境 | JSDOM: Node.js内でJavaScriptによってシミュレートされたブラウザ環境。(実際のブラウザでも使用可能) | 実際のブラウザ: PlaywrightやCypressを使用して、実際のブラウザ(Chrome、Firefoxなど)を操作します。 |
| 「ビジュアル」テストの意味 | 計算後のスタイル(getComputedStyle)やDOMの属性をチェックします。例:expect(element).toHaveStyle('background-color: red')。 | 実際のピクセルレベルでのレンダリング。スタイルのチェックだけでなく、ビジュアルリグレッションテストも可能で、スクリーンショットの比較を通じてピクセルレベルの意図しない変更を検出します。 |
| デバッグ体験 | ターミナルの出力、console.log、またはNode.jsのデバッガに基づきます。コンポーネントが実際にどのように見えるかは確認できません。 | インタラクティブで視覚的なデバッグ。ブラウザでテストの各ステップをリアルタイムで観察し、一時停止、ステップ実行、そのステップでのDOMの状態を確認できます。これはStorybookの大きな利点です。 |
| 焦点 | 「ユーザーが使えれば、それは機能している」。主にARIAロール、テキストコンテンツ、状態に焦点を当て、すべてのユーザー(支援技術を使用するユーザーを含む)にとってコンポーネントが機能的であることを保証します。CSSクラス名や色の値など、具体的な実装の詳細をテストすることは意図的に避けます。 | 「機能するだけでなく、見た目も正しくなければならない」。同様にインタラクションに焦点を当てますが、視覚的な忠実度を同等に重要な位置に引き上げます。Storybookのストーリー自体が視覚的な基準となります。 |
| ワークフロー | 1. テストファイル内でコンポーネントをrenderする。<br>2. ユーザーのインタラクション(userEvent)をシミュレートする。<br>3. DOM構造やモック関数に対してアサーションを行う。 | 1. すでに視覚化されたStoryが存在する。<br>2. play関数を記述してインタラクションを定義する。<br>3. インタラクションがStorybookのキャンバス上で自動的に実行される。<br>4. アサーションがブラウザで実行される。 |
| 限界 | 実際の視覚的なレイアウトをテストすることはできません。レンダリングエンジンがないため、JSDOMは要素がCSSによって隠されているか、画面外に移動しているか、font-familyが正常に読み込まれたかなどを認識できません。 | テスト速度が比較的遅い(ブラウザを起動する必要があるため)。 |
個人的な感想#
testing-library + JSDOMの長所#
- より軽量で高速。ブラウザと比較して、JSDOMは軽量なテストのために作られているため、速度面では文句なしです。
- モジュールのモックが比較的容易。
- スナップショットで出力データを取得できる。
Storybookインタラクションテストの長所#
- Storyとテストを統一管理できる。テストで作成済みのStoryを再利用でき、単体テストのためにわざわざDOMを構築する必要がありません。
- 視覚的なテスト。ブラウザでテストのステップを明確に確認でき、テストを書く上でも非常に便利です。
- 各ステップの途中でビジュアルリグレッションテストを行うことができる。
testing-library + JSDOMでのテストに適しているケース#
長所を考慮すると、ユーザー操作後のデータに関心があるテストはtesting-libraryで書くのに適していると個人的に考えます。つまり、testing-libraryはユーザー操作を提供するツールとして利用し、expectによるアサーションはあまり行わないケースです。
- UI上のクリックによってAPIが呼び出されるテスト。
- フォーム関連で、ユーザー操作後に得られるフォームデータに注目するテスト。
- モジュールのモックが必要なテスト。
- (公式では非推奨ですが)実装で使用されている関数をスパイして確認するテスト。
- 軽量で、testing-libraryで完結できるテスト。
Storybookインタラクションテストに適しているケース#
上記の長所に基づき、ほとんどのテストはStorybookインタラクションテストで書くのに適していると、私は大まかに考えています。
特に、ユーザーのインタラクション関連で、操作ステップのUI要素や見た目が重要なテストです。
Storybookインタラクションテストに適していないケース#
- データ操作が複雑で、
expectによるアサーションが困難なテスト。 - データ出力に主に関心があるテスト。
- 頻繁にモジュールをモックする必要があるテスト。
結論#
インタラクションテストの作成をまだ試していない段階ですが、筆者の大まかな考えとしては、インタラクションテストは**WYSIWYG(見たままが得られる)**の特性を持つため、可能な限りtesting-library + JSDOMをインタラクションテストで置き換えることは合理的だと考えます。その副作用として生じる実行時間の問題については、まだ実際にテストしていないため、その深刻度は未知数です。実際に試してみてから、また更新したいと思います。
関連リンク: Storybookのスナップショットテストを Chromatic に置き換えて、コンポーネントのテストをより快適にする - Cybozu Inside Out | サイボウズ株式会社のエンジニアブログ