Wallaby.jsを使ってフロントエンド開発のテストを効率化しよう

Findy Team+でフロントエンドエンジニアをしている 川村(@peijun333)です。

Findy では、フロントエンドのコード品質と安定性を確保するために Jest などのテストフレームワークを積極的に活用しています。通常、Jest は CLI から実行してテスト結果をコンソールで確認しますが、コマンドを用意する手間や、テスト経過のデバッグのために都度 console.log などでその内容を確認しなければならずとても不便です。

そこで、今回はテストの自動化とリアルタイムなフィードバックを提供する JavaScript の統合テストツールである Wallaby.js を紹介します。Wallaby.js を導入することで、開発効率の向上が期待できます。

Wallaby.js とは?

Wallaby.js is an integrated continuous testing tool for JavaScript. It runs your tests immediately as you change your code (you don’t even have to save the file) and displays various results (including the code coverage, error and console messages) right inside your code editor, next to your code. Wallaby.js is great for doing JavaScript TDD (Test-driven development) or BDD (Behavior Driven Development), but it works great for other approaches as well.

公式ドキュメントより Introduction: What is Wallaby.js?

Wallaby.js は JavaScript の統合テストツールで、次のような機能があります。

  • リアルタイムなフィードバック
    • Wallaby.js は、テストの実行に手動のステップが不要なため、コードを変更するとリアルタイムで結果をフィードバックしてくれます。
  • エディタ内でのテスト結果表示
    • Wallaby.js は、コードエディター内にテスト結果を表示してくれます。エラーやカバレッジ情報などが、コードの隣に直接表示されるため、エディタから離れることなく、テスト結果を確認できます。
  • デバッグ機能
    • Value Explorer を使用することで、任意の場所で値を調査し、呼び出し元のコールスタックを確認できます。また、コンソールログをエディタ上に表示する機能も備えており、デバッグプロセスをスムーズにします。

また、サポートしているコードエディタとテストフレームワークが豊富で導入がとても容易です。

Wallaby.js は OSS 開発では無料で利用できますが、商用利用の場合は有料です。ファインディではフロントエンドを開発するメンバーにアカウントを発行しています。

前提条件

ここから実例を交えて機能を紹介します。実例に使った動作環境は以下の通りです。

Wallaby.js の導入に関する詳細な手順や設定方法については、公式ドキュメントを参照してください。

VS Code でテストの修正

Wallaby.js は サポートされているエディタ上で動作します。今回は VS Code で普段テストを書いている様子を紹介します。

以下は、GraphQL を用いてユーザーデータを正しく取得できているか確認するテストの例です。

+ expect(result.current.users).toStrictEqual([{ id: '1', name: 'name' }]);
- expect(result.current.users).toStrictEqual([{ id: 1, name: 'name' }]);

id が number 値ではなく string の '1' であるべきなのですが、上記動画の例では誤った結果を書いています。 Wallaby.js ではこのようにテスト結果がコードエディタ上(VS Code の例)にリアルタイムに反映されます。

予想される結果と実際の結果が異なる場合、赤色のテキストでエラー文が表示されます。そこにカーソルを合わせることで、具体的に差分内容を VS Code 上で確認できます。

- Expected  - 1
+ Received  + 1

  Array [
    Object {
-     "id": 1,
+     "id": "1",
      "name": "name",
    },
  ]

また、VS Code 上では左側の余白に四角のテストカバレッジインジゲーターが赤色から緑色に変わった様子が確認できたかと思います。これにより視覚的にテストのカバレッジを確認できます。

詳細: Editor Code Coverage Indicators

この機能のメリットは、エディタ上でテストの操作が完結でき、さらにリアルタイムに優れている点です。テストの実行や結果、失敗時のエラー原因の迅速な特定、ナビゲーション、差分ビューなど、すべてをエディタ上で行うことができます。

Wallaby.js はリファクタリングに強い

特にリファクタリングでは、 Wallaby.js の機能の 1 つである「リアルタイムにテスト結果がフィードバックされる機能」が発揮されます。

以下は ユーザーデータから空の name を取り除けることを確認するテストの例です。

テスト結果と関数のインターフェイスはそのままで、ロジックのみをリファクタリングしています。こちらの例のように、リアルタイムにテストが失敗し、リファクタリング後ファイルを保存せずともテストが成功しました。

このようにリアルタイムにテストが落ちてくれるので、関数の影響範囲がわかりやすく、テスト結果からすぐにフィードバックされることで効率よくリファクタリングが可能です。

スナップショット の更新

次にスナップショットテストの更新について紹介します。スナップショットテストとは、UI が予期せず変更されていないかを確かめるテストです。Findy Team+では @testing-library/react を使い コンポーネントのスナップショットを撮影し、必要に応じて差分を更新しています。

はじめに Wallaby.js を使わずにJest のスナップショットを更新する手順をみてみましょう。

次の例は title と description を表示するコンポーネントのスナップショットテストです。

import { render } from "@testing-library/react";

import { TestComponent } from "./test.component";

const props = {
  title: "title",
  description: "description",
};

describe("TestComponent", () => {
  it("表示確認", () => {
    const { asFragment } = render(<TestComponent {...props} />);

    expect(asFragment()).toMatchSnapshot();
  });
});

実装コード

type Props = {
  title: string;
  description: string;
};

export const TestComponent = ({ title, description }: Props) => {
  return (
    <>
      <div>{title}</div>
      <div>{description}</div>
    </>
  );
};

@testing-library/react の render 関数を使いスナップショットを撮影しています。このテストを初めて実行すると次のようなスナップショットファイルが作成されます。

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`TestComponent 表示確認 1`] = `
<DocumentFragment>
  <div>
    title
  </div>
  <div>
    description
  </div>
</DocumentFragment>
`;

仕様の変更により、description を表示するタグを <span> タグに変更してみます。

- <div>{description}</div>
+ <span>{description}</span>

そしてテストを再度実行すると UI の差分によりテストが失敗します。

 ● TestComponent › 表示確認

    expect(received).toMatchSnapshot()

    Snapshot name: `TestComponent 表示確認 1`

    - Snapshot  - 2
    + Received  + 2

      <DocumentFragment>
        <div>
          title
        </div>
    -   <div>
    +   <span>
          description
    -   </div>
    +   </span>
      </DocumentFragment>

      12 |     const { asFragment } = render(<TestComponent {...props} />);
      13 |
    > 14 |     expect(asFragment()).toMatchSnapshot();
         |                          ^
      15 |   });
      16 | });
      17 |
 › 1 snapshot failed.

今回は description を表示するタグは div ではなく span タグが正しいのでスナップショットを更新する必要があります。

Jest でスナップショットを更新するには --updateSnapshot-u オプションを使い更新します。 (または ウォッチモードで対話的に更新)

以上の手順を踏むことでスナップショットの更新が完了します。

今回は 1 ファイルのみの修正で変更差分も軽微なものですが、複数のテストが失敗した場合、目的のテストを見つける手間と時間がかかります。また、エディタからターミナルに移動しコマンドを実行する必要があるため、コンテキストを切り替える必要があり、多くの時間が消費されます。

次に Wallaby.js を使ったスナップショットの更新をみてみましょう。

エラーにカーソルを合わせ、差分を確認した後、右上のカメラアイコンをクリックしただけです。

失敗したテストをエディタで確認し、スナップショットを直接更新するだけなので、目的のテストを見つける手間も時間もかかりません。

ユニットテストにおけるデバッグ機能の紹介

Wallaby.js には Value Explorer という機能があります。これを活用することで、デバッグ作業を効率化でき手動でのログ出力作業を大幅に削減できます。

また、テスト実行ログに記録されている値に対して、呼び出し元(実装コード)の Call Stack を調べることもできます。

Value Explorer を使用することで、標準出力をコードに仕込まずともデバッグ作業を可能にしますが、使い慣れている console.log を使ったデバッグも Wallaby.js は対応しています。エディタ上にログの結果を表示できるため、コードとログを同時に確認しながらデバッグ作業を行うことができます。

Wallaby.js は多様なログの確認方法があり、当記事では紹介しきれないので気になる方はチェックしてみてください。

Introduction: Advanced Logging

Wallaby.js を使ってみて

Wallaby.js を使ってみて特にリアルタイムにテスト結果がフィードバックされる点はとても便利だと感じました。Wallaby.js を導入する前はファイル単位やテストケース単位で、テストを実行させる必要があり時間がかかっていましたが、リアルタイムにテスト結果を反映してくれるので実装に集中して開発を進めることができます。リファクタリングでは、リファクタリングの前後でインターフェイスが保たれているかをエディタ上のテストカバレッジインジゲーターから確認できる点は Wallaby.js の強みだと感じました。

FAIL PASS

また、Smart Start という機能により、コードの変更により影響を受けた箇所のみ自動的にテストが実行されるので、とても高速にストレスフリーで動作してくれます。Findy Team+のように大規模なプロジェクトでも高速に動作してくれるのは非常に強みだと思います。

最後に

今回の記事では、JavaScript の統合テストツールである Wallaby.js について紹介しました。

Wallaby.js の導入により、開発プロセスの効率化やコードの品質・安定性の向上が期待できます。是非、Wallaby.js を活用して、よりスムーズなフロントエンド開発を実現してみてください。

現在、ファインディでは一緒に働くメンバーを募集中です。

興味がある方はこちらから ↓ herp.careers