Closed6

フロントエンドテスト - Kent C Dodds

はるはる

テストの種類

End to End

極力モックを排除し、実際のブラウザを通してフロントエンドからバックエンドまでを本番に近い状態でテストする。
ユーザーのように振る舞うヘルパーロボットが実際にブラウザ上のボタンをクリックするなど、実際の操作を行う。
ほぼすべての範囲をテストでき最も信頼性が高い反面、テストの作成、実行環境等によるコストが高く、テストの実行スピードも遅いためフィードバックも遅く、テスト自体も壊れやすい。

テスト対象

  • アプリケーション全体

テスト範囲外

  • (ほぼ)なし

使用ツール

  • Cypress / Playwright

インテグレーションテスト

複数のユニットが互いに統合されることをテストする。
React Hooks(Vue Composables) や サードパーティー製のライブラリを含めて極力モックを排除することでより多くの問題を発見できる。

テスト対象

  • MSWでネットワークをモックしてページ単位
  • 結合された状態のコンポーネント

テスト範囲外

  • バックエンドと正しいデータを渡しているか
  • エラーに対応しているか

使用ツール

  • Testing-Library
  • Vitest / Jest
  • MSW (ネットワークモック)

ユニットテスト

依存関係がないユニットをテスト、またはテスト用にモックされたユニットをテストする。
フィードバックが早くコストも低いが、依存関係を Mock しており信頼性が低い。
複雑なロジックや、エッジケースをメインにテストする。

テスト対象

  • コンポーネント単位
    • コンポーネントを描画しない Shallow Mount は行わないことを推奨している。
  • React Hook(Vue Composable)単位
  • 関数
  • クラス

テスト範囲外

  • バックエンドと正しいデータを渡しているか
  • エラーに対応しているか
  • 依存関係の呼び出しが正しいか

ツール

  • Jest / Vitest

Static テスト

タイポや型エラーなどのコード上のエラーをキャッチする。

テスト対象

  • 全コード

テスト範囲外

  • バックエンドと正しいデータを渡しているか
  • エラーに対応しているか
  • 依存関係の呼び出しが正しいか
  • ビジネスロジック

ツール

  • ESLint
  • Stylelint
  • TypeScript の型システム

Kent C Dodds: The Testing Trophy and Testing Classifications
Kent C Dodds: Static vs Unit vs Integration vs E2E Testing for Frontend Apps

はるはる

テスティングトロフィー

対費用効果で着眼したときにどのテストに比重を置くべきかをトロフィーの面積で表したもの。

上にあるものほど高コストで壊れやすく、失敗の解析、修正の時間がかかるが、多くの範囲をテストでき信頼性が高い。(コスト:テストの作成コスト、テストを実行するためのCI環境など)

E2Eテストは最も信頼性が高いが、テストの作成やテストの実行環境も含めて高コスト。またテスト実行に時間がかかりフィードバックが遅く、エラーの解析にも時間がかかる。

ユニットテストはテストを作成するのもかんたんで、実行速度も早くフィードバックも早い。しかし Mock するため依存関係をテストすることができず、信頼性が低い。

結合テストはその中間。極力 Mock を排除することで多くのケースをカバーすることができ、信頼性が高く、コストもE2Eほどではない。
最も対費用効果が高いため、結合テストに比重をおいて作成することを推奨している。


Kent C Dodds: The Testing Trophy and Testing Classifications

はるはる

実装詳細のテストを避ける

実装の詳細とは?

  • ユーザー(アプリケーションやコードを使うエンドユーザー、デベロッパー)が見みない、使用しない、知らないこと
    • 変数名
    • メソッド名
    • ボタンを押されるとなんのメソッドが呼ばれるかなど

なぜ実装の詳細をテストしてはいけない?

  • 偽陰性/偽陽性が発生しやすい
    • 偽陰性
      • アプリのコードは間違っていないのにエラーが出る
      • 正しくリファクタしただけでテストが壊れる
    • 偽陽性
      • アプリのコードが間違っているのにエラーが出ない
      • リファクタを誤ってもテストがエラーが検出されない

実装の詳細を避けてテストを行うには?

  • ユーザーが使う方法と同じようにテストを行う。

    テストがソフトウェアの使用方法に似ているほど、信頼性が高まります。

  • エンドユーザー
    • 画面上のUIを操作し、結果が画面上に反映されることを確認する
  • デベロッパー
    • コンポーネントの Props に値を設定し、画面上の値が更新される
    • Store の値を dispatch し、画面上の値が更新される

Kent C Dodds: Testing Implementation Details

はるはる

テストユーザーを避ける

コンポーネントが関心を持つべきユーザー

エンドユーザー

  • 実際にアプリケーションを使うユーザー
  • ブラウザ上に表示されたコントロールを操作し、状態を変更する(UX)
  • UXを変更する場合はリリースノート、トレーニング資料を更新するなどが必要になる。

開発者

  • コンポーネントを使って開発を行うユーザー
  • コンポーネントが公開するAPI(ストア、Propsなど)をもとに操作を行う
  • API 変更する場合はコンポーネントの使い方を変える必要がある

内部リファクタリングのようにUXやAPIを変更しなければ追加の更新は不要となり、コンポーネントに閉じた変更となり追加の変更は不要となる。

テストユーザーとは

  • エンドユーザー、開発者が使う方法とは別の方法を使ってコンポーネントを使う
    • 変数名やメソッド名、ボタンが押されたらどのようなメソッドが呼ばれる、など

テストユーザーに向けたテストを行ってしまうと内部リファクタリングによってテストが壊れたり、エラーを検出できなかったりするのでテストユーザーに向けたテストは行わないようにする= 実装詳細のテストを避ける。


https://kentcdodds.com/blog/avoid-the-test-user

はるはる

何をテストするべきか

ユースケース

  • テスト対象がユーザーにどのように使われるか
  • テストは変更が加わってもそのユースケースをサポートしていることを保証するために書く

ユースケースカバレッジ

  • どれぐらいのユースケースをテストしているか
    • 自動で検出する方法はない
  • コードカバレッジが100%でもユースケースカバレッジが100%になるとは限らない
    • コードではなくユースケースをテストしよう

Kent C Dodds: How To Know What To Test

このスクラップは2023/11/18にクローズされました