Header image

クラッソーネの開発者がエンジニアリングに関することもそうでないことも綴っています!

Hotwire StimulusのテストをVitestで書いてみた

Hotwire StimulusのテストをVitestで書いてみた

こんにちは!クラッソーネでエンジニアをしている町田です。

前回のテックブログ投稿(pathpidaをSapper(Svelte)で使ってみた)から2年と数ヶ月ほど経っており、かなり長い間投稿できずにいたのですが、久しぶりに記事を執筆したいと思います。

今回のテックブログでは、Railsのフロントエンド開発で使用しているStimulusVitestでテストする方法について書いてみました。

Stimulusを選んだ理由は、HTMLに再利用可能なロジックを小さく追加でき、シームレスにHTMLとJavaScriptを繋げられる点と、RailsでJavaScriptを扱う際のデファクトスタンダードになりつつあるためです。一方、Vitestは実行速度が高速で、Stimulusと同様に現代のテストフレームワークのデファクトスタンダードになっているため、採用しました。

今後Stimulusを使っていきたい方や、Stimulusのテストコードを書きたい方の参考になれば幸いです。

※ VitestやStimulusについての詳細な説明はこの記事では行いませんので、公式ドキュメントや他の参考記事を参照してください。

環境

使用しているライブラリについては以下のバージョンを利用しています。

  • @hotwired/stimulus: v3.2.2
  • vitest: v1.6.0
  • jsdom: v24.0.0

必要なライブラリをインストールします。パッケージマネージャはお好みのものに適宜書き換えてください。

npm install @hotwired/stimulus
npm install --save-dev vitest jsdom

Stimulusコントローラの追加

以下にサンプルとなるStimulusコントローラを追加します。以下のコードはStimulusのドキュメントから引用しています。
引用:https://stimulus.hotwired.dev/handbook/hello-stimulus

// hello_controller.ts
import { Controller } from "@hotwired/stimulus";

export default class extends Controller {
  greet() {
    console.log("Hello, Stimulus!", this.element);
  };
};

Vitestのセットアップ

vitest.config.tsをプロジェクトディレクトリのトップに作成します。

import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    // StimulusはDOMを操作するため、jsdom環境を設定します。
    environment: 'jsdom',
    // テスト対象のファイルパターンを指定します。
    include: ['src/**/*.{test,spec}.{js,ts}']
  },
});

テストコードの追加

次に、Stimulusコントローラのテストコードを追加します。今回はHelloControllergreetメソッドが呼ばれることを確認します。

※ 以下は例としてのテストコードですので、実際のテストコードはテストしたいロジックに合わせて適宜書き換えてください。

// hello_controller.test.ts
import { vi, describe, beforeEach, afterEach, test, expect } from 'vitest';
import { Application } from "@hotwired/stimulus";
import HelloController from './hello_controller';

describe('HelloController', () => {
  let application: Application;

  beforeEach(() => {
    document.body.innerHTML = `
      <div data-controller="hello">
        <input type="text">
        <button id="button" data-action="click->hello#greet">Greet</button>
      </div>
    `;
    application = Application.start();
    application.register("hello", HelloController);
  });

  afterEach(() => {
    application.stop();
  });

  describe('#greet', () => {
    test('greetメソッドが呼ばれること', () => {
      const helloController = application.getControllerForElementAndIdentifier(document.querySelector('[data-controller="hello"]'), 'hello') as HelloController;
      const spy = vi.spyOn(helloController, 'greet');
      const buttonElement = document.getElementById('button') as HTMLButtonElement;
      buttonElement.click();

      expect(spy).toHaveBeenCalled();
    });
  });
});

テスト結果

テストがパスし、HelloController#greetが呼ばれていることが確認できました。

testing_result

まとめ

今回の記事では、StimulusとVitestを使用してフロントエンドのロジックをテストするためのシンプルな環境を構築し、実際にテストコードを書くプロセスを解説しました。

Rspecに慣れている方はSystem Specでテストしてもいいですが、小さく早くテストを回したい方にはVitestでのテストコード作成をおすすめします。

Stimulusのテスト環境を構築したい方や、既存のプロジェクトに効率的なテストを導入したい方に、少しでも役立つ情報が提供できたなら幸いです。