浜松のWEBシステム開発・スマートフォンアプリ開発・RTK-GNSS関連の開発はお任せください
株式会社シーポイントラボ
TEL:053-543-9889
営業時間:9:00~18:00(月〜金)
住所:静岡県浜松市中区富塚町1933-1 佐鳴湖パークタウンサウス2F

【JavaScript】Jestでカスタムエラーメッセージ的なものを出力

 Jest は JavaScript のユニットテスト用のライブラリです。
Jest · 🃏 Delightful JavaScript Testing
 JavaScript を主に見た目に関する部分で使っている時は Jest 単体ではそれほど出番がありません。これは不正な見た目の状態を生成する JavaScript の状態をあらかじめ厳密化することが難しいことが原因です。いざブラウザ上で動かすと Jest 実行環境とブラウザ環境の差異に引っかかったりもします。そういった見た目のテストをする時は Selenium 等を介した実際にブラウザを動かすテストを用いるのがいいです(自分が Jest 単体で使う時がコマンドラインのみ、Selenium を使う時はサーバーサイドもまとめてテスト、なのでこう書きましたが、実際は Jest + Selenium のツールもありました)。
Seleniumブラウザー自動化プロジェクト | Selenium
 Jest が特に威力を発揮するのはロジック周りです。これは例えば状態遷移の複雑なフローが正しいことを確かめる、会計等の複雑な計算処理が正しいことを確かめる、といった JavaScript の内部で完結する処理です。こういった複雑な処理(分岐の多い処理)についてテストすべきケースは大抵多数になり、画面越しに手作業で正しいことを確かめるのは非常に手間です。Jest を用いてコンソール上で自動でまとめて高速に確かめたくなります。

 Jest は基本的にユーザが明示的にテストケースを定めることを期待しています。例えば次です。

// Jest のバージョンは 26.6.3 です。この記事の以降に出てくる Jest も同じです
describe(__filename, () => {
  test('tmp', async () => {
    for (let i = 0; i < 10000; i++) {
      expect(Math.round(Math.random())).toBe(0);// ランダムな値でテスト
    }
  });
});
// エラー発生時は以下の様にどのテストケースのどこでエラーが起きたか、と toBe 相当の期待と実際の値のみ表示
  ● フルパス/example.test.ts › tmp

    expect(received).toBe(expected) // Object.is equality

    Expected: 0
    Received: 1

      20 |   test('tmp', async () => {
      21 |     for (let i = 0; i < 10000; i++) {
    > 22 |       expect(Math.round(Math.random())).toBe(0);
         |                                         ^
      23 |     }
      24 |   });
      25 |

      at 相対パス/example.test.ts:22:29
      at 相対パス/example.test.ts:8:71

  テストケースで十分に説明されることが期待されており、この expect にも toBe にもエラーメッセージを決める機能がありません。jest はどのテストケースの何行目で間違ったかを伝えるのみです。多くの場合はこれで十分なのですが、エラーメッセージを自由に定義してテスト失敗時の情報をより詳しく知りたい時もあります。例えばそれはランダムテストをする時です。
 エラーメッセージを定義できないつくりはランダムにテストケースを生成するテストを Jest を介して行いたい時には特に困りものです。このつくりではどのテストケースでエラーが発生したのかわかりません。ランダムテストを起点とする修正は、ランダムテストでエラー発生→エラー発生ケースを用いたテストケースを作成→作成したテストケースを用いてテストと修正、といった流れで行いたいのですが、これではエラー条件がわからずエラーケースが作れず困ってしまいます。
 Jest でカスタムエラーメッセージ的なものを使うコードは次の様につくれます。

test('tmp', async () => {
    for (let i = 0; i < 10000; i++) {
      const message = {// 表示用メッセージをあらかじめ用意
        name: 'tmp',
        v: -1,
      };
      try {
        // テストに使う値など表示したいものを message 変数に含ませる
        message.v = Math.random();
        expect(Math.round(message.v)).toBe(0);
      } catch (e) {
        // テスト失敗時は例外を用いて処理が中断される
        // 例外をキャッチした時にのみ何が起こっていたかの message を表示
        console.error(JSON.stringify(message, null, 2));
        // 例外を本来の例外処理に戻すためにそのまま throw
        throw e;
      }
    }
  });

 こうするとテスト失敗時にのみ次の様なエラーメッセージを表示できます。

  console.error
    {
      "name": "tmp",
      "v": 0.8963176109090807
    }

      27 |         expect(Math.round(message.v)).toBe(0);
      28 |       } catch (e) {
    > 29 |         console.error(JSON.stringify(message, null, 2));
         |                 ^
      30 |         throw e;
      31 |       }
      32 |     }

 一応これで自由にエラー時の状態を表示できるのですが、このまま使うには例外によるテスト失敗ログと console.error の表示が実行順にならず読み難いという問題があります。これの対策はテストケース単位でのテストの実行です。テストケース単位の実行によって、表示されるものは全て単一のテストケースに関わるものとわかるようになります。コマンドのが煩雑になりますが、これは IDE によるテスト実行補助でまかなうと意識せずに済み楽ができます。

  • この記事いいね! (0)