リリース前は気が重い——『単体テストの考え方・使い方』第1部を読んでまとめてみた

リリース前は気が重い——『単体テストの考え方・使い方』第1部を読んでまとめてみた

はじめに

こんにちは。株式会社アイスリーデザインの芹田です。

チームメンバーと話していると、こんな声掛けがあります。

「単体テストは、ちゃんと書いてますか?」

もちろん、書いています。CI でも回っています。
それでも、リリース前は気が重い。そんな状態に、心当たりはないでしょうか。

私はその不安感を解消するために、Vladimir Khorikov 著『単体テストの考え方・使い方』を読み、適切な単体テストが実装されているか判断できるようにしたいと考えました。

本記事は、その読書メモの第1部を、私自身が単体テストを深く知るために、私なりに噛み砕いてまとめたものです。

私が理解しやすい言葉に変更している箇所もあるため、事実と異なって見える箇所があるかもしれませんが、私のように「単体テスト」とは何かを改めて理解したいと感じている方のきっかけになれれば幸いです。

単体テストは、何のために書くのか?

「テストはあるのに、変更が怖い」という不安に対して、第1章は次の整理を示してくれています。

  • 単体テストは、うまく設計すれば「資産」になるが、雑に増やせば「負債」にさえなること。
  • 目的は、機能を足し続けても品質を保てること、プロジェクトの成長を止めないこと。

単体テストの価値

第1章では、次の3つに整理されています。

価値現場で言うと
リグレッション(デグレ)防止直したつもりが別のところを壊した、を自動で検知できる
リファクタリング設計を直すときの「大丈夫かな…」が減る
生きた仕様書「この機能、何をする想定?」がテストコードで確認できる

本当に必要最小限まで削ぎ落としたまとめ方をしてくれているので、シンプルで理解しやすかったです。

知っておくべき2つの落とし穴

ここまで読むと「じゃあたくさん書いて網羅すればいい」と思いがちです。(実際、私も近い思考になっていました…)
しかし、そこを下記2つの観点ではっきり止めています。

【落とし穴①:質の低いテストは、書いても書かなくても大差ない】

場合によっては、書かない方がマシなことすらあります。コストは最初の実装だけではなく、変更のたびの修正、誤検知の調査、実行時間の増加も含めて考える必要があります。

【落とし穴②:網羅率は「合格証」にならない】

何をテストしたいかがずれたまま指標だけを追うと、カバレッジは上がるのに価値は上がりません。網羅率は、テストが足りないときの「警告灯」にはなり得ますが、品質の「合格証」にはなりません。

では「良いテスト」とは?

私が期待したのは、明確に良し悪しを決めることができる指標を使って判断することでしたが、残念ながらテストの良し悪しを自動で点数化する方法はないようです。

しかし、落とし穴を避けたうえで、著者が挙げる良いテストスイートの条件は次の3つと表現していました。

  1. テストが開発の流れの中で回っている(CI など)
  2. 大事なロジックやビジネスルールに絞って書かれている
  3. 保守コスト最小・価値最大のバランスが取れている

さらに、数字がきれいでも変更が楽にならないなら、スイートの設計を見直すサインである可能性が高いこと。網羅率とテストの質は別物として考えること。どちらも「たしかに!」と納得できる詳細が書かれていました。

「1クラス=1テスト」は、いつ正解になるのか?

第2章で、いよいよ「単体テストとは何か」の定義に入ります。

「1クラスにつき1テスト」——これは、正しいのでしょうか。実は学派によって、答えが変わるようです。

まず、単体テストが満たすべき条件は次の3つです。

  • 1つの振る舞いを検証していること
  • 実行時間が短いこと
  • 他のテストケースから隔離されていること

ロンドン学派は「単体=1つのクラス」と捉え、不変依存を除く依存はすべてテストダブルに置き換える考え方です。一方、古典学派は「単体=1つの振る舞い」と捉え、共有依存(DB・ファイル・外部 API など)に限りテストダブルに置き換える考え方です。

観点ロンドン学派古典学派
「単体」が指すもの1つのクラス1つの振る舞い
テストダブルに置き換える対象不変依存を除くすべて共有依存のみ
隔離の考え方クラス単位で依存を差し替えるテスト対象を共有依存から切り離す

ロンドン学派のやり方は、境界がわかりやすい反面、テストがクラスの内部実装の話に寄り、振る舞いの「物語」が見えにくくなることがあります。
古典学派は、依存が複雑なクラスのテストは大変ですが、それ自体が設計上の問題のサインになる、とも書かれています。

当社の既存プロジェクトをいくつか見てみたところ、プロジェクトによって多少の違いはあるものの、おおむね古典学派に沿った実装が多かったです。
第1部を読み終えた時点では、私も古典学派の考え方に賛成する立場です。
振る舞い単位で切り、共有依存だけをテストダブルに置き換える。この方針が、私たちの実態とも合っていると感じました。

また、実行順序によって結果が変わってしまう、あるいはテスト実行後に DB やファイルを削除する「後始末」が必要になる単体テストは、見直す価値がありそうです。

第2章では、テストの骨格として「AAA パターン」(Arrange=準備、Act=実行、Assert=確認)も紹介されます。第3章では、この書き方をさらに掘り下げていきます。

読みやすいテストは、仕様の補助資料になる

第3章のタイトルは「単体テストの構造的解析」です。地味に見える章ですが、テストが負債化するかどうかの分岐点は、意外とここにあるかもしれません。

  • フェーズが入り乱れたテスト
  • if でシナリオを分岐させたテスト
  • 名前が test1 のままのテスト

これらはどれも動きます。しかし、何を保証しているのかが伝わりません。1つずつ見ていきましょう。

フェーズが入り乱れたテスト

AAA パターンでは、次の2点が大切です。

  • 準備・実行・確認のフェーズを混ぜない
  • フェーズの間は空白行で区切る(各フェーズ内で空白行が必要になる場合は、フェーズの先頭にコメントを付ける)

そうすることで、シンプルで読みやすいだけでなく、次のように構造を見直すきっかけを得られやすいというメリットがあります。

  • 【Arrange(準備)】が肥大化していないか。→肥大化したら、テストクラスの private メソッドやファクトリクラスへ切り出す
  • 【Act(実行)】は基本1行か。→複数行なら、API のカプセル化が破綻していないか
  • 【Assert(確認)】は検証対象が1つの振る舞いに絞られているか
  • 【後始末】がないか。→必要になる場合は、テストダブルに置き換える対象を見直す

if でシナリオを分岐させたテスト

テスト内に if を書くのはアンチパターン。なぜなら、1本のテストに複数の物語が入ってしまうからです。

名前が test1 のままのテスト

テストメソッドの命名もおろそかにしてはなりません。

  • テストメソッド名は、非開発者にも何を検証しているか伝わるようにする。
  • 長くなる場合は _ で区切って読みやすくする(例: returns_zero_when_cart_is_empty)。
  • テスト対象は、変数名を sut(System Under Test)に統一する

どれもシンプルですが効果は大きく、ぜひ取り入れたいと思いました。

まとめ・すぐに使えるチェックリスト

第1部では、なぜ単体テストをするのか、どのような単体テストが良いのかを見てきました。

私にとって非常に参考になる内容で、これまでより自信を持って品質活動を進められると感じました。

単体テストの知見が豊富な方ばかりではないと思います。私を含め、そうした方が現場ですぐ活用できるポイントを、以下にチェックリストとしてまとめました。参考にしていただければ幸いです。

単体テストの方針(何をテストするか)

テストの狙いが正しいかを見る観点。コードを読めなくても、PM・QA が判断に加われます。

  • ビジネスロジックや重要なルールがテストされているか
  • 重要度の低いコードまで網羅的にテストしようとしていないか
  • 1本のテストが1つの振る舞いに絞られているか
  • 網羅率を「品質の合格証」にしていないか(あくまで下限チェックに留めているか)

単体テストの運用観点(コードを読まずに判断できる)

テストの外側から見える情報だけで判断できる観点。技術に詳しくなくても確認できます。

  • 命名がわかりやすいか
  • テスト対象が sut 変数で明示されているか
  • 実行時間が短いか
  • CI に組み込まれ、繰り返し実行されているか

単体テストの技術的チェック(コードを読んで判断する)

テストコードの中身を読める人が見る観点。

  • AAA に則っているか(準備・実行・確認を混ぜず、空白行で区切る)
  • Act が基本1行か(超えるなら API のカプセル化を疑う)
  • テスト内に if でシナリオを分岐させていないか
  • テスト後に DB やファイルを削除するなどの「後始末」が入っていないか(必要なら共有依存の扱いを見直す)
  • 共有依存(DB・ファイル・外部 API など)だけをテストダブルに置き換えているか

本記事は、Vladimir Khorikov 著『単体テストの考え方・使い方』(マイナビ出版)第1部を読んだメモをもとに執筆しています。

Cursor Commands活用術:単体テスト自動生成プロンプト

ABOUT US
Yosuke_Serita
テレビ制作会社、通信業界、人材業界、IT業界にて、営業や管理、育成と幅広い業界と業種を経験。 QAに強いPMを目指し、2023年冬から株式会社アイスリーデザインにジョイン。 DXパートナーとして、品質の観点からお客様の課題解決プロジェクトを推進できるよう日々研究中。