💭

テストってどんな基準で書くのだろう?

2025/02/10に公開

はじめに

テストを書こうと思って学習したものの、実務では「そんな時間ないよ…」という空気を感じる

テストが書かれているプロジェクトに関わったことがなかったので

  • jestを使った単純な関数のテスト
  • Storybookを使ったコンポーネントのテスト
  • ビジュアルリグレッションテスト
  • E2Eテスト(playwright)

を学習してみた。

「こんなこともできるのか!?」と感動する一方で、いざ実務で自動テストの話が出ると
「誰が書くの?忙しくてそんな時間ないよ」という空気に...
確かに、 すべての機能にすべての種類のテストを書くのは、コストや保守性の面から現実的ではない様に感じる。
では、どのテストをどれくらい書けばいいのか?
どういった基準を設けて、テストの優先順位を決めればいいのか?
気になったため、自分なりに整理してみた。

そもそも、なんでテストを書くの?

「なんで自動テスト、書くんだろうね?」「変化を可能とする能力を備えておくため」

https://logmi.jp/main/technology/330970

影響範囲が分からないごちゃごちゃの設計になってしまい、想定外のバグが出そうで小さな変更でも誰も触りたがらず、気合い入れないと取りかかる気になれない雰囲気を経験したことがある。
そのため、「変化を可能とする能力を備えておくため」が自分の中ではしっくりきた。

記事を読んで、テストは「バグをなくすため」だけでなく、安心して開発を進めるためのものなのだと感じた。

どうやって優先順位をつける?(テスト戦略の考え方)

https://logmi.jp/main/technology/330972
すべての機能にすべての種類のテストを書くのは、開発スピードを落としてしまうし現実的ではない。
記事で紹介されていたGoogleでの基準で自分なりにまとめてみた。

テストの基準を明確にする

人それぞれユニットテストの定義が異なってくるので、Googleではテストを「どれくらいの範囲で実行されるか」という基準で Small(小)・Medium(中)・Large(大) の3つに分けている。

Small Test(小)

  • 1つのプログラムの中だけで動くテスト
  • 実行が速く、すぐに結果が分かる
  • 他のシステムに影響されにくい
    例: 関数やクラスの動作確認(ユニットテスト)

Medium Test(中)

  • 1台のパソコンの中で完結するテスト
  • 複数の機能や部品(モジュール)がうまく動くか確認できる
  • 速度と実用性のバランスが良い
    例: APIとデータベースのやりとりをチェックするテスト

Large Test(大)

  • 複数のサーバーやシステムをまたぐテスト
  • 実際の環境に近い動作を確認できる
  • 実行に時間がかかり、コストも高め
    例: サーバー同士の通信や、マイクロサービス全体の動作確認

このように、Googleでは 「どの範囲で動作するか?」 でテストを分類し、それぞれの特性に応じて使い分けている。

テストサイズとテストの範囲(スコープ)の関係

Googleでは、テストの大きさ(Small/Medium/Large) と テストの範囲(ユニット/インテグレーション/E2E) の関係を 3×3のマトリクス で整理している。

テストサイズ × テストスコープの対応表

ユニット(小さな部品) インテグレーション(組み合わせ) E2E(全体の動作)
Small ◎(理想的) ○(コスパ良い) △(難しい)
Medium ○(場合による) ◎(理想的) △(コスト高)
Large ×(非推奨) △(非効率) ◎(必要)

理想的な組み合わせ

  • ユニットテストは Small(単体の関数・クラスをサクッとテスト)
  • インテグレーションテストは Medium(複数の機能の連携を確認)
  • E2Eテストは Large(本番環境に近い動作チェック)

理想的ではないが、コスパが良いエリア

  • Small × インテグレーション
    例: API の結合テスト(単一プロセス内で完結)
    高速で信頼性も高い
  • Medium × インテグレーション
    例: DB・バックエンド・フロントの統合テスト(1台のPC内で完結)
    パフォーマンスとカバー範囲のバランスが良い

コスパが悪い・避けるべきテスト

  • Large × ユニットテスト
    例: 小さな関数のテストなのに、外部のネットワークやサーバーを呼び出す
    実行が遅く、不安定になりやすい
  • E2E テストを増やしすぎる
    例: すべてのパターンをE2Eテストでカバーしようとする
    → テストが遅くなり、メンテナンスが大変に!
    解決策: 重要なユーザー操作(CUJ: Critical User Journey)に絞る

優先順位の考え方

ソフトウェアの開発では、コードを変更したらテストを実行して問題がないことを確認する必要があるが、テストを毎回すべて実行すると時間がかかりすぎるため、効率的にテストを実行する工夫が必要になってくる。
テストの基準を明確にしておくと、段階的にテストを実行する考え方ができる。

効率的なテストの流れ

  1. まずはSmall Testを実行する
    • 実行速度が速く、フィードバックループが短い
    • 修正の影響範囲を小さい
    • バグを早期発見できるため、後から修正するよりコストが低い

ここで失敗したら、Medium Test以降を実行する意味が薄れると考えることができる。

  1. Small Testが成功したら、Medium Testを実行

    • Medium Testは、複数の部品が正しく動くかをチェックする
    • 実行には時間がかかるが、すべての機能が連携して動くかを確認できる
  2. 最後にLarge Testを実行

    • ここで初めて、本番に近い環境でシステム全体をテストする
    • 実行時間が一番長いので、できるだけ重要なテストだけに絞る

テストサイズによるピラミッド

      ▲ Large Test(少)→ システム全体の動作確認(E2Eテスト相当)
     ▲  Medium Test(中)→ モジュール・サービス間の連携確認(インテグレーションテスト相当)
    ▲   Small Test(多)→ 個々の機能・ロジックの検証(ユニットテスト相当)
  • Small Testを最優先し、基盤を固める
  • Medium Testで統合の安定性を担保する
  • Large Testは重要なフローだけに絞り、並列実行を検討する

テストサイズに基づくピラミッドを構築することで、信頼性が高く効率的なテスト戦略を実現できる。

まとめ

  • テストの実行環境の範囲と複雑さでサイズ分類(small, midium, large)
  • Small Testを最優先し、基盤を固める
  • テストは一気に実行するのではなく、Small → Medium → Large の順に進める
    • バグは早く見つけるほど、修正コストを低くできる
    • テストサイズが大きくなるほどコストが高くなる
  • Small Testが失敗したら、他のテストを実行する前に修正する
  • Large Testはコストがかかるため、本当に必要なものだけに絞る
    • 失敗したら致命的なフロー(ログインや決済)

こうすることで、実行時間を短縮することもでき、素早くバグを発見できるテスト戦略を実現できる。

基準はある程度イメージできる様になったが、どのテストを書けばいいかの判断ができるようになるには実際に手を動かして経験を積み上げていかないといけないなと感じた。

Discussion