• トップ
  • ブログ一覧
  • Testing Trophyとは〜フロントエンドテストについて学んでみた〜
  • Testing Trophyとは〜フロントエンドテストについて学んでみた〜

    モーリー(エンジニア)モーリー(エンジニア)
    2023.07.31

    IT技術

    突然ですがフロントのテストってどんな感じで書けば良いのでしょうか?

    私は特にフロントのテストについて調べないまま我流で今までやってきてました。😅

    他のコンポーネントなどの参照を全てモックして各コンポーネントごとに純粋なUnitテストを書くのがいいのか?

    Unitテスト以外(Integrationテスト、E2Eテスト)はどの段階で書けばいいのか?どれくらいの量書けばいいのか?

    色々と日々の業務で疑問を抱えることが多くなり、フロントのテストについて調べていた時にTesting Trophyという考え方を見つけたのでここでまとめてみたいと思います。

    Testing Trophyとは?

    Testing Trophyとは、Kent C. Doddsという方が考案したフロントエンドのテストにおける必要十分な各種類のテストの配分を表した概念になります。

    (余談ですがこの方はReact Testing Libraryを作った方です。これはためになりそうな予感がしますね)

    さてTesting Trophyの詳細を説明する前に、先にみなさんが馴染みがあるであろう概念のTesting Pyramidを紹介します。

    上の図がTesting Pyramidを表したものになります。図の下から上に行くほどにテストの実装工数・テスト処理時間などが増えコストが高いものとなっていきます。そのためこの図から分かるようにTesting Pyramidでは一番下のUnitテストを一番多く書き、上に行くほどテスト量を減らしていくことを勧めています。私もこの図は何度か目にしたことがあり、今までの業務の場面でもほとんどをUnitテストで書き、Unitテストをある程度書いた後に少しのIntegrationとほんの少しのE2Eテストを書く(もしくは書かない😅)というようにしてきました。

    それでは次にTesting Trophyの図をみてみましょう。

    Testing TrophyではIntegration Testの層が一番厚くなっています。

    これは先ほど話した、コスト(実装コストやリソースのコスト)以外のTesting Pyramidで上の層に行くほど高くなるものに起因しますそれはテストの信頼度です。

    単体テストのみでは単一の機能同士が結合した時に正しく動作をするかの確証を得ることができません。

    そのため機能同士の繋がりをテストできるIntegrationテストを一番多く書くことが、コストとテストの信頼度のトレードオフを考える上で一番有用であるとTesting Trophyは示しています。

    Reactなどのフレームワークを用いたプロジェクトではコンポーネント同士の繋ぎ合わせでアプリケーションができていますので、この概念はとても腑に落ちますね。

    Integration Testってどんなテスト?

    さぁ、Testing Trophyに倣うとIntegration Testを多く書けば良いそうです。

    ですが、実際Integration Testってどんな感じで書けばいいのでしょうか?

    コンポーネント同士の繋がりをテストするため、コンポーネントが複数合わさってできているコンポーネントをテストすればいいのでしょうか?どれくらいの大きさのコンポーネントならIntegrationテスト??

    そもそもUnitテストを多く書くのに慣れてきた私には多少なりともと実装コストなどの面からIntegration Testを多く書くことに不安があります。

    また色々と疑問が出てきてしまいあてもなくネットをさまよっていると、Martin Fowlerという方のこんな記事に出会いました。

    そこには、

    Testing PyramidのUnit TestもTesting TrophyのIntegration TestもどちらもUnit Testだ

    と書かれていました。(わかりやすいように多少大袈裟に言っています🤗)

    記事の内容を説明しますと

    Martinさん曰く、UnitテストにはSociable TestSolitary Testがあるようです。(下の図)

    Sociable Testは他の機能との繋がりを(可能な限り)保ったまま単一の機能のテストを行う手法です。

    それと相反するのがSolitary Testで、こちらはモックによりテスト対象と他との繋がりを隔絶する手法です。

    Testing TrophyでIntegration Testと呼ばれているのはこのSociable Testに当たり、Testing PyramidでのUnit TestはSociable / Solitary Test(どちらか、または両方)になります。

    Martinさんがこう考えたのは、Testing Trophyを支持する人の議論の中でしばしばモックの使いすぎとそれが引き起こす問題についてが問題視されていたからだそうです。

    確かにTesting Trophyの概念を作ったKent C. Doddsさんのブログを見てみるとこう書かれていました。

    the biggest thing you can do to write more integration tests is to stop mocking so much stuff.

    訳: より多くの統合テストを書くためにできる最大のことは、多くのものをモックするのをやめることだ。

    なるほど、なるほど。Integration Testを書くにはモックを極力やめればいいそうです。

    可能な限りモックを行わないでテストを実装することが、テスト対象と他の機能の繋がりをも検証できる信頼度の高いテストにつながるということのようです。

    おまけ

    Testing Trophyとは直接は関係しませんが、Kent C. Doddsさんのブログを読んでいるととてもためになる話に出会いましたので最後におまけとして載せておきます。

    それは、

    実装の詳細をテストすることはよくない

    というものです。

    テストを書く上で実装の詳細のテストを書くことは下記二つの点からよくないそうです。

    1. Can break when you refactor application code. False negatives
      リファクタリングをする時にテストがこれてしまう可能性がある
    2. May not fail when you break application code. False positives
      コードが壊れていても失敗しない可能性がある

    1. リファクタリングをする時にテストがこれてしまう可能性がある

    処理の結果を変えずにコードの内容の修正を行った時にテストが壊れてしまう可能性があります。

    テストは書くことも大変ですが、メンテナンスするのもとても大変ですよね。

    リファクタしただけなのにテストが壊れてその修正にまた時間がかかるなんてことは業務の中でもある程度起こってしまうことですので、変数の値など(実装の詳細)を確認するテストを書くのではなくユーザーが確認できるものに対してのテストを書くように意識するべきです。

    2. コードが壊れていても失敗しない可能性がある

    正しい動作なのにテストが壊れてしまうことは起きてほしくないですが、もっと起きてほしくないのが正しくない動作なのにテストが壊れないことですよね。

    propsでは渡したisModalOpenの値はボタンをクリックすると想定通りtrueに変わってテストは通るが、内部で別の変数を参照しているためモーダルが開いていない。なんてことが起こらないためにもやはりユーザーが確認できる結果をテストすることが重要だそうです。

    最後に

    この記事で参考にしたKent C. Doddsさんのブログはフロントエンドのテストに関わる話やReactの話など色々とためになることを多く書かれていましたので、一度訪れてテストなどについて深く学んでみるのも良いと思います。

    また、紹介したTesting TrophyではIntegration Testを多く書くことを勧めていましたが、最近はE2Eテストを書くライブラリ(cypressなど)の向上で昔よりE2Eテストを簡単に書くこと容易になったためE2Eテストの層を厚くすべきだとの意見も見られました。(参考動画

    自分自身E2Eテストをあまり書いたことがなく、cypressなどのライブラリを使ったことはありませんでしたので次はcypress使ってみた系の記事でも書いてみたいと思っております。

    ではではまた次の記事で👋

    モーリー(エンジニア)

    モーリー(エンジニア)

    おすすめ記事