公開日:2022.01.20

テストから逃げてきた人が考えるフロントエンドテスト戦略についての考察

テクログjavascript

前提

以下の状況/課題を踏まえた上で、フロントエンドのテスト戦略について考察していく。

  • 新規プロジェクトである
  • 中~大規模プロジェクトである
  • 実装規模に見合わない納期である
    • 様々な条件から優先度を決定し、テストを実施するスコープを決める必要がある
  • テストコードの保守/運用コストを意識し、保守/運用方法についても決定する必要がある
  • プロジェクトにアサインするメンバーがテストに慣れていない(テスト経験が豊富な開発者が存在しない)
    • テストの学習コストを測る必要がある
  • 開発チームの人数が10人以下
  • 外部API連携を行う必要がある
    • 決済系
  • フロントエンドに採用する技術
    • Next.js
      • ISR/SSR/CSR/GraphQL(Apollo Client)/REST API(SWR)/NextAuth
    • React
      • Functional Component/React Hook Form
    • TypeScript
    • CSSModule

注:このプロジェクトはフィクションです。

なぜテストを行うのか

  • バグを防ぐ
    • バグが起きるとサイト自体あるいはサイトの重要な機能が停止し、サイトのユーザーおよびサイトの運営会社に損失を与えることになる
  • リグレッションを防ぐ
    • これもバグと同じで、サイト自体あるいはサイトの重要な機能が停止し、サイトのユーザーおよびサイトの運営会社に損失を与える可能性がある

つまりサイトのユーザーおよびサイトの運営会社に損失を与える前に、それを起こす要因を潰しておくことが重要なミッションである。
そのためにテストを行い、その要因を検知する必要があると考える。

テストを行うことによるメリット/デメリット

テストを行う必要性が分かったところで、次にプロジェクトにテストを入れるメリットとデメリットについて整理をしてみる。

メリット

  • 事前に重大なバグを検知できる確率が上がる
  • リグレッションテストが同じ精度で行える
  • テストをパスしているコードについては、その結果についての信頼性を証明できる

デメリット

  • (テストの書き方を理解していない場合)テストを書くための学習コストがかかる
  • テストを書く分の工数が増えるので、プロジェクトの進捗に影響する
  • テストコードのメンテナンスを継続的に行っていく必要がある

何にでもメリットとデメリットは存在する。
これらを考慮した上で、何をテストするのか、どうやってテストをするのか、どこまでテストをするのかを選択していく。

何をテストするのか

ReactのメジャーなテスティングライブラリであるReact Testing Libraryの思想からヒントを得てみる。

まずはテストを避けるべき対象について以下のように述べている。

テスティングライブラリでは、テストするコンポーネントの内部など、実装の詳細をテストしないことを推奨しています(それでも可能ではありますが)。このライブラリの指針は、ウェブページがユーザーによってどのように操作されるかに酷似したテストに重点を置くことを強調しています。

ユーザーが実際に何を操作してどのような結果になるかを重要視している。
つまり、実際のwebサービスの使われ方にできる限り近づけたテストを書くことが必要である。

また、基本の思想として以下のように述べている。

テストがソフトウェアの使用方法に似ていればいるほど、より高い信頼性を得ることができます。

なので、テストをする対象としては以下で良さそうである。

  • ユーザーの操作
  • ユーザー操作による状態変化の結果

どうやってテストをするのか

何をテストするのかは決まったので、次にどうやってテストをするかを考えていく。
その前に、フロントエンドのテストにはどのような種類があるのかを整理していく。

フロントエンドテストの種類

React Testing Libraryの開発者であるKent C. Dodds神が提唱しているThe Testing Trophyを参考にする。
testing-trophy (1).png

The Testing Trophyに倣うと、フロントエンドのテストは4種類に分類できる。

  • End to End
    • いわゆるE2Eテスト。ユーザーのように振る舞い、アプリをクリックし、正しく機能するかどうかを検証するテスト
  • Intergration
    • 複数のユニットが調和して動作することを確認するテスト
  • Unit
    • 分離された個々の部品が期待通りに動作することを確認するテスト
  • Static
    • コードを書きながら、誤字・脱字をキャッチするテスト
  • ユーザーの操作
  • ユーザー操作による状態変化の結果

このテスト方針に沿うと、優先度はEnd to End > Intergration > Unit > Staticで良さそうである。

各テストを何を使ってテストするかについては、以下のような感じにすれば良さそうである。

  • End to End > Cypress
  • Intergration > React Testing Library
  • Unit > React Testing Library
  • Static > ESLint,TypeScript

どうやってテストをするかについては、大枠のイメージがついた。

次にどこまでテストをするのかを考えていく。

どこまでテストをするのか

ここで言うどこまでは各4つのテスト手法でテスト対象とする範囲である。

ここで前提をもう一度思い出す。

  • 実装規模に見合わない納期である
    • 様々な条件から優先度を決定し、テストを実施するスコープを決める必要がある
  • テストコードの保守/運用コストを意識し、保守/運用方法についても決定する必要がある
  • プロジェクトにアサインするメンバーがテストに慣れていない(テスト経験が豊富な開発者が存在しない)
    • テストの学習コストを測る必要がある

頭が痛くなるが、上記を踏まえた上で、まず簡単に決められるものから決めていく。

  • Static
    • ESLint,TypeScriptを使用してコーディングに対してテストを行いたい。
      • 範囲はコミットされるts/tsxファイルすべてで問題なさそうである。husky+lint-stagedを使用すれば、初回の設定以外はコストがほぼかからなくなる。
  • End to End
    • 4つの手法の中で最もテスト方針に近いものである。最重要であるがゆえに最も多くのテストを書くことになると思われる
      • 範囲は全てのユーザー操作で問題ない。と言いたいところだが実装時間は無慈悲にも有限である。ここはプロジェクトによって変わってくるが、その操作に失敗したら即座にサイトの機能が落ちるまたはその機能が落ちるとあらゆるページで連鎖的にバグがでるこの2つを念頭において対象となる操作を絞り込むのが現実的なラインだろうか。
  • Unit
    • 機能(ロジック)単体をテストしたい。
      • 優先度では下から2つ目なので、多くのテストは必要ない。コンポーネントからロジックのみをカスタムHooksとして切り出し、その中でもその操作に失敗したら即座にサイトの機能が落ちるまたはその機能が落ちるとあらゆるページで連鎖的にバグがでるを念頭に置いて対象とするロジックを選択することが望ましい。
  • Intergration
    • 個人的に最も悩みそうなテストである。
      • こちらも、その操作に失敗したら即座にサイトの機能が落ちるまたはその機能が落ちるとあらゆるページで連鎖的にバグがでるを念頭に置いて対象とする結合ロジックを選択することが望ましい。

結論

  • テスト優先度はEnd to End > Intergration > Unit > Static
  • テストコードが多すぎるとメンテナンスコストが肥大していくので、プロジェクトの初期段階では落ちたらビビるもの考え抜いてから、スモールスタートでやっていくのが良い。
  • テストは大事。だがプロジェクトを納期までにリリースすることはもっと大事。納期伸びて

おわりに

完璧を目指すよりまず終わらせろ
“Done is better than perfect”
-マーク・ザッカーバーグ

参考文献

この記事を書いた人

棒人間

入社年2019年

出身地東京都

業務内容開発

特技または趣味スケボー

棒人間の記事一覧へ

テクログに関する記事一覧