ジュニアエンジニア向け 開発フロー入門シリーズ(全8回)
第6回:テスト:テストは仕様書であり、網である

テストは仕様書であり、網である:何を守り、何を守らないか【第6回】

テストを書けと言われても、何をテストすればよいか分からない。書いても安心できない。カバレッジ100%を目指すべきなのか——この記事を読み終えると、テストを「壊れたことに気づく網」であり「実行できる仕様書」として捉え、どこに網を張り、どこに張らないかを自分で判断できるようになります。ジュニアエンジニア向け開発フロー入門シリーズ(全8回)の第6回です。

🎯 テーマの主役:「テスト」——壊れたことに気づく網

今回の主役はテストです。一言で言えば、テストとは「期待と違う動きを、出荷する前に検知する仕組み」です。

日常の例えで言うなら、です。網には目の粗さがあります。細かい網は小さな魚も捕まえられますが、重く、張るのに手間がかかります。粗い網は軽く、広く張れますが、小さい魚は逃げます。テストも同じで、細かい網(単体テスト)と粗い網(全体のテスト)を組み合わせて、効率よく捕まえるのが設計です。1種類の網で全部を捕まえようとすると、重くなって誰も張らなくなります。

もう1つの顔が「実行できる仕様書」です。テストコードには「この入力のとき、こうなるはず」と書かれています。これは文書としての仕様ですが、普通の仕様書と違って古くなると壊れます。つまり、仕様と実装がずれたら気づけるのです。第2回で作った受け入れ条件が、そのままテストになるのはこのためです。

第5回のレビューは「人の目」でした。今回のテストは「機械の目」です。第1回の関門6の出口条件は「期待と違う動きを検知できる網がある」でした。この記事では、その網の張り方を具体化します。

この関門を通せるようになると、次の4つができるようになります。第一に、何をテストし、何をテストしないかを判断できること。第二に、バグを見つけたときに、再発を防ぐテストを書けること。第三に、カバレッジに振り回されないこと。第四に、テストしにくいコードを見て、設計の問題に気づけることです。4つ目は、テストが設計の検査装置として働く場面です。

テストを網にたとえた概念イラスト 細かい網は小さな魚を捕まえるが重く、粗い網は軽いが小さい魚を逃がす。2つを組み合わせることが必要だと示す図。 1種類の網で全部を捕まえようとすると、重くなって張られなくなる 細かい網=単体テスト 小さな魚も捕まる(境界値・例外) 速い・数が多い・原因が特定しやすい 細部を守る。数を多く張る 粗い網=全体のテスト 小さな魚は逃げる(細部は見えない) 遅い・数が絞られる・原因が分かりにくい つながりを守る。数を絞る 2つを重ねる 細かい網(単体) 粗い網(結合・全体) 逃げる魚を減らしつつ、重くしない

動機:テストの「正体」が分からないと、迷い続ける

テストについての悩みは、たいてい3つの迷いに集約されます。第一に、何をテストすればよいか分からない。先輩に「テストも書いておいて」と言われ、とりあえず動くことを確認するテストを書いたが、これでよいのか分からない。第二に、書いても安心できない。テストは通っているのに、レビューでバグを指摘される。第三に、カバレッジという数字に追われる。「カバレッジ80%以上」という目標が設定され、意味のあるテストより、行を通すだけのテストが増える。

これらの迷いは、テストの目的が1つに定まっていないことから来ます。テストには少なくとも3つの役割があり、それぞれ違う網の張り方を要求します。役割を区別しないと、「全部を守ろうとして重い網」か「何も守れない軽い網」のどちらかになります。

この記事の仮説はこうです。テストで迷ったときの判断基準は「これは何を守る網か」である。もしこれが正しければ、「何をテストするか」は網の役割と目の粗さから逆算でき、カバレッジの数字に振り回されなくなります。

🔍 検証①:テストは「正しさの証明」ではなく「間違いの検知」である

まず、テストについて最も誤解されやすい点を確認します。テストが通ったことは、正しさの証明になりません。これは有名な指摘で、計算機科学者エドガー・ダイクストラの言葉として広く引用されています。「テストは欠陥の存在を示すことはできるが、欠陥が存在しないことは証明できない」(Testing shows the presence, not the absence of bugs)。

なぜ証明にならないのか。理由は単純で、テストは書かれた範囲しか確認しないからです。100個の入力パターンを試しても、101個目で壊れるかもしれません。テストが通ったという事実が保証するのは、「確認した範囲では、期待通りだった」だけです。

この理解は、テストの使い方を根本から変えます。テストの目的は「正しさを証明すること」ではなく「期待と違う動きを早く見つけること」です。だから、間違えやすい場所に重点的に網を張るのが合理的になります。均等に全部を守ろうとするのは、もっとも間違えにくい場所に、最も高いコストを払うことになります。

誤解実際実務での帰結
テストが通れば正しい確認した範囲で期待通りだっただけ「テストは通っています」は安心の材料であって、保証ではない
テストを増やせば品質が上がる増えるほど保守コストも増えるメンテナンスされないテストは、いずれ嘘をつく
テストは品質保証部門の仕事テストは書いた本人の理解を映す実装者が書くからこそ、意図の記録になる
カバレッジが高ければ安心実行されたことと、検証されたことは違う数字は手段。目的は「壊れたら気づくこと」

🔍 検証②:テストの3つの役割

テストには、次の3つの役割があります。この3つを区別すると、テストを書く目的がはっきりします

① 回帰の検知。これが最も基本的な役割です。「昨日まで動いていたものが、今日は動かない」を検知します。第4回で見たように、変更は常に起きます。そして、変更が別の場所を壊すこと(デグレード)は日常的にあります。テストの網は、変更のたびに張り直すのではなく、変更が壊したものを自動で知らせます

② 仕様の記述。テストコードは「この入力のとき、こうなる」という実行可能な仕様です。第2回の受け入れ条件がそのままテストになるのはこの役割です。文書の仕様は古くなっても気づけませんが、テストは古くなると失敗するので、必ず気づけます。仕様書としてのテストは、腐らない仕様書です。

③ 設計の検査。これが最も見落とされます。テストしにくいコードは、設計に問題があるサインです。たとえば、1つの関数がデータベースと外部APIと画面に依存していると、テストを書くために全部を準備しなければなりません。これは第3回で見た境界が曖昧な状態です。テストを書こうとして「書きにくい」と感じたら、それはテストのスキル不足ではなく、設計のシグナルである可能性があります。

役割何を守るか網の張り方書くタイミング
① 回帰の検知既存の動作壊れたら困る箇所に、確実に変更の前後。バグ修正時は必須
② 仕様の記述期待される振る舞い受け入れ条件(第2回)をそのまま実装の前後どちらでも。先に書くと設計が固まる
③ 設計の検査変えやすい構造境界の前後を分けて実装中。「書きにくい」に気づいたとき
テストの3つの役割 回帰の検知、仕様の記述、設計の検査という3つの役割と、それぞれの網の張り方を示す図。 テストの役割を区別すると「何を書くか」が決まる ① 回帰の検知 昨日まで動いていたものが 今日は動かない、を検知する 変更が壊したものを自動で知らせる バグ修正時は必須 最も基本的な役割 壊れたら困る箇所に確実に ② 仕様の記述 「この入力ならこうなる」を 実行できる形で書く 文書の仕様は古くなっても気づけない テストは古くなると失敗する=気づける 受け入れ条件がそのままテストになる 腐らない仕様書 ③ 設計の検査 テストしにくいコードは 設計に問題があるサイン 依存が絡んだ関数は準備が多い = 境界が曖昧(第3回) 「書きにくい」はスキル不足ではなく 設計からのシグナル

🔍 検証③:網の目の粗さ——テストピラミッド

テストは目の粗さで分類できます。細かい順に、単体テスト(1つの関数やクラス)、結合テスト(モジュール同士のつながり)、全体テスト(利用者の操作を再現するE2Eテスト)です。そして、数をどう配分するかに定石があります。

定石はテストピラミッドと呼ばれます。土台に単体テストを大量に、中間に結合テストを適量、頂点に全体テストを少数置く形です。逆の形(全体テストが多く、単体テストが少ない)は、形が似ていることからアイスクリームコーンと呼ばれ、アンチパターンとされます。

なぜピラミッドが良いのか。理由は3つです。①実行速度:単体テストはミリ秒、E2Eテストは数十秒〜数分かかります。E2Eが中心だと、テスト全体が遅くなり、実行が億劫になります。②壊れやすさ:E2Eテストは画面の変更やネットワークの影響を受けやすく、実装と無関係に落ちます。落ちる理由が「本物のバグ」でないテストが増えると、誰もテスト結果を信じなくなります③原因の特定:単体テストは「どこが壊れたか」を正確に指します。E2Eが落ちても、原因は多数の候補のどれかです。

種類守る範囲実行時間落ちたときの原因特定
単体テスト関数・クラスの振る舞いミリ秒多い(土台)即座に分かる
結合テストモジュール間・API・データの流れ適量(中間)境界をたどれば分かる
E2Eテスト利用者の操作の流れ数十秒〜分少数(頂点)候補が多く、時間がかかる
(アンチパターン)E2Eが中心非常に長いE2Eが多い分からない。やがて無視される

ここでも、第5回のレビューの優先順位と同じ構造が現れていることに気づきます。速く・安く・原因が分かる確認を土台に大量に置き、遅く・高く・原因が分かりにくい確認を少数にする。この原則は、開発フロー全体を貫いています。第1回のコスト原則(早く見つけるほど安い)の、テスト内での表現です。

テストピラミッドとアイスクリームコーンの対比 単体テストを土台に多く、E2Eを少数にするピラミッド型と、その逆のアイスクリームコーン型を比較する図。 推奨はピラミッド。逆さにすると遅く・壊れやすく・原因不明になる テストピラミッド(推奨) E2E(少数) 結合(適量) 単体(土台・大量) 速い・壊れにくい・原因が分かる 確認の頻度を高くできる アイスクリームコーン(逆パターン) 単体(少数) 結合(適量) E2E(大量) 遅い・壊れやすい・原因が分からない やがて「落ちてるだけ」と無視される

🔍 検証④:どこに網を張るか——境界と異常系に集中する

次に、具体的に何をテストするかです。「全部」は不可能なので、優先順位を決めます。経験的に効果が高いのは、境界値と異常系です。

境界値とは、条件の切り替わる値です。たとえば「100件まで表示する」という仕様なら、99件・100件・101件が境界です。バグは「ちょうど境界」に潜みやすいことが経験的に知られています(「>」と「>=」の取り違えなど)。第2回で扱った「0件はエラーではない」という決定も、境界の仕様です。

異常系とは、期待通りでない入力や状況です。空の値、最大値を超える値、権限がない利用者、外部APIが失敗したとき、途中で通信が切れたとき。正常系は利用者も開発者も頻繁に触るので、実装の早い段階で自然に直ります。しかし異常系は誰も触らないまま本番に出ます。そして本番で初めて発動し、最も見つけにくいタイミングで障害になります

優先度テストする対象理由
1(最優先)過去に壊れた箇所同じ原因で再び壊れる。再発防止の網が最も効く
2境界値(ちょうどの値・1つ違い)比較の誤りが集中する。発見が最も安い
3異常系(空・最大・権限なし・外部失敗)本番でしか発動しない。見つけるのが最も高い
4複雑なロジック(条件分岐が多い)組み合わせが多く、人の確認では追えない
5外部に公開している約束(境界)変更の影響が外に及ぶ(第3回)
優先度低単純な表示・外部ライブラリの動作・頻繁に変わる見た目壊れにくい。または変更が多く、テストが負債になる

一方で、テストしないという判断も必要です。単純な表示の切り替え、外部ライブラリが保証している動作、頻繁に変わる画面の見た目——これらに網を張ると、変更のたびにテストを直すことになり、テストの維持コストが効果を上回ります。網は、守る価値のあるものを守るために張るのです。

🔍 検証⑤:バグを見つけたら、まず再現テストを書く

バグを見つけたときの手順には、定石があります。それは、直す前に、そのバグを再現するテストを書くことです。手順は次のとおりです。

  1. 失敗するテストを書く:バグを再現する最小の入力を選び、テストにする。このとき、テストは必ず失敗します(まだ直っていないので)。失敗を確認することで、テストが本当にそのバグを捉えていることを確かめます。
  2. 直す:テストが通るように修正します。
  3. テストが通ることを確認する:失敗が成功に変わることを確認します(赤から緑へ)。
  4. 他のテストが壊れていないことを確認する:修正が別の場所を壊していないかを見ます。

この手順の価値は、「直った」を証明できることです。修正の前にテストを書かないと、修正後に「本当に直ったのか」「たまたま動いているだけではないか」が分かりません。さらに、そのテストは未来の再発を防ぐ網として残ります。バグ修正のたびに網が1つ増えるので、時間とともに壊れにくいコードになっていきます

バグを見つけたときの手順 失敗するテストを先に書き、修正し、通ることを確認し、他のテストへの影響を確認する4段階を示す図。 直す前にテストを書くと「直った」を証明できる ① 失敗するテスト バグを再現する最小の入力 をテストにする 必ず失敗することを確認 (テストがバグを捉えている証拠) ② 修正する テストが通るように コードを直す 原因を特定してから (対症療法で隠さない) ③ 通ることを確認 失敗が成功に変わることを 確認する(赤→緑) これが「直った」の証拠 テストは網として残る ④ 他を壊していないか 既存のテストを全部実行する (回帰の確認) 修正が別の場所を壊すのは 日常的に起きる バグ修正のたびに網が1つ増える。時間とともに壊れにくいコードになっていく この手順を省くと、「直ったつもり」が本番で再発する 第1回のコスト原則:テストで見つけたバグの修正は、本番後の100分の1以下で済む

🔍 検証⑥:カバレッジは「測っているもの」を誤解しやすい

カバレッジ(網羅率)は、テストが実行したコードの割合です。よく「カバレッジ80%以上」という目標が設定されますが、この数字には大きな落とし穴があります。

落とし穴は、「実行された」と「検証された」は違うという点です。たとえば、次のテストはカバレッジを上げますが、何も検証していません

  • 関数を呼ぶだけで、結果を確認しないテスト
  • 例外が起きないことだけを確認するテスト(起きてほしくない例外を見逃す)
  • 期待値を「実際の出力」そのままで書いたテスト(バグごと固定してしまう)

つまり、カバレッジは「テストの量」を測りますが、「テストの質」は測りません。100%のカバレッジでもバグは残りますし、60%でも重要な箇所を守っているテストのほうが価値が高いことがあります。

実行されたことと検証されたことの違い コードが実行されただけの状態と、結果が検証されている状態の違いを示し、カバレッジは前者しか測らないことを示す図。 カバレッジが測るのは「実行された割合」。品質は測れない 実行されただけのテスト 関数を呼ぶ(結果を確認しない) 例外が出ないことだけを見る 実際の出力をそのまま期待値に書く カバレッジは上がる。守っているものは少ない 検証しているテスト 境界値(ちょうどの値・1つ違い) 異常系(空・権限なし・外部失敗) 過去に壊れた箇所の再発防止 数は少なくても、守る力が強い 使い方:目標値として追わず、0%の箇所を探す・下がった変更を確認する

では、カバレッジをどう使えばよいのか。実務的には2つの使い方が有効です。第一に、極端に低い場所を探すこと。カバレッジ0%の重要な処理は、一度も確認されていないという危険信号です。第二に、変化を監視すること。「カバレッジが下がった変更」は、テストなしで追加されたコードの可能性があります。目標値として追うのではなく、危険の探索と監視に使うのが正しい使い方です。

誤った使い方何が起きるか正しい使い方
目標値を設定して達成を目指す行を通すだけのテストが増え、質が下がる危険な箇所(0%)を探す材料にする
カバレッジが高い=品質が高いと考える誤った安心感。重要な異常系が抜けたまま出荷される「実行」ではなく「検証」に注目する
下がることを禁止するテストしにくい箇所の変更が滞る下がった変更を確認する(テストなしの追加を検知)

結果:テストを書く/書かないの判断表

ここまでの内容を、判断表にまとめます。テストを書く時間は有限なので、どこに張るかの判断が最も重要です。

場面書く/書かない理由
バグを修正した必ず書く(再現テスト)再発防止。修正の証明になる
新しい機能を作った書く(受け入れ条件から)仕様の記録になる。第2回の条件をそのまま
条件分岐が多いロジック書く(境界値中心)人の確認では追えない。バグが集中する
外部失敗の処理書く(異常系)本番でしか発動せず、見つけるのが最も高い
公開している境界(API等)書く変更の影響が外に及ぶ。契約の記録になる
単純な表示の切り替え書かない(またはE2Eで1本)壊れにくく、変更が多い
外部ライブラリの動作書かないライブラリ側の責任。自分の変更で壊れない
頻繁に変わる見た目書かない(目視確認で代替)テストが負債になる。変更のたびに直すことになる

考察:テストは「書いた人の理解」を映す鏡である

ここからは、検証では扱いきれなかった解釈を述べます。テストについて最も面白い性質は、テストの質が、書いた人の理解の質をそのまま映すことだと考えます。なぜなら、テストを書くには次の3つを言葉にしなければならないからです。どんな入力があるか(境界と異常系の理解)、何が期待されるか(仕様の理解)、どこまでを1つの単位とするか(設計の理解)。この3つが曖昧だと、テストは書けません。

逆に言えば、テストが書けないときは、理解が足りていないということです。「テストを書く時間がもったいない」と感じる場面でも、書けない理由を調べると、要件の曖昧さ(第2回)や境界の曖昧さ(第3回)が見つかります。テストは、理解の穴を見つける装置でもあるのです。

そしてAI時代には、この性質の意味が変わると考えられます。AIがテストコードを生成できるようになると、「テストを書く」作業自体は速くなります。しかし、何を検証すべきかを決めるのは人間です。境界値はどこか、異常系は何か、何をもって成功とするか——これは、第2回の受け入れ条件の作成そのものです。AIが生成したテストが意味のある網になるかは、人間が与えた観点の質で決まります。逆に、観点のないままAIに「テストを書いて」と頼むと、通るだけで何も守らないテストが大量に生まれます。カバレッジは上がり、安心感だけが膨らみます。これが、私が考えるAI時代の最も危険なパターンの1つです。

さらに、テストはチームの記憶としても働きます。なぜこの境界値がテストされているのか、なぜこの異常系が重要とされているのか——テストの名前とコメントに理由を書いておくと、過去の障害の知識が次世代に伝わります。「このテストは2026年の◯◯障害の再発防止」と書かれたテストは、単なる確認ではなく、組織の学習の記録です。

📌 注目ポイント

  • テストは正しさの証明ではない。「欠陥の存在は示せるが、不在は証明できない」
  • 役割は3つ:回帰の検知・仕様の記述・設計の検査。3つ目は「テストしにくい=設計が悪い」のシグナル
  • 網はテストピラミッドの形に張る。単体を土台に多く、E2Eは少数
  • 集中すべきは過去に壊れた箇所・境界値・異常系。均等に守ろうとしない
  • バグを見つけたら、直す前に再現テストを書く。失敗を確認してから修正する
  • カバレッジは質を測らない。「実行」と「検証」は違う。危険の探索と監視に使う
  • テストしない判断も設計。壊れにくいもの・頻繁に変わるものには張らない
  • テストが書けないときは、要件か設計の理解が足りていない

💡 活用事例:1つの単位変換が1億2,500万ドルを消した

テストの役割を最も劇的に示す事例の1つが、1999年の火星気候探査機(Mars Climate Orbiter)の喪失です。

1999年9月23日、NASAの火星気候探査機は火星に到着する際に通信が途絶し、失われました。事故調査委員会の報告書(1999年11月)によれば、原因は単位の取り違えでした。探査機の軌道制御に関する計算で、ソフトウェアの一部がヤード・ポンド法の単位(重量ポンド力・秒)で出力した値を、別の部分がメートル法の単位(ニュートン・秒)として扱っていました。小さな推力のずれが積み重なり、探査機は予定より低い高度に投入され、失われました。損失は約1億2,500万ドルとされています。

この事故で注目すべきは、単位の取り違え自体は、テストで防げた種類の欠陥だという点です。入出力の単位を明示する、境界で単位を検証する、結合テストで実際の値を確認する——いずれかが機能していれば、火星到着の数か月前に地上で発見できていました。事故調査委員会の報告では、検証と確認(Verification and Validation)のプロセスが不十分だったこと、明確なインタフェース仕様が欠けていたことが指摘されています。これは、第3回で扱った境界(インタフェース)の設計と、今回の結合テストの欠如が、同じ事故の両面として現れた例です。

もう1つ重要なのは、これが「優秀なエンジニアが集まっていれば防げた」種類の問題ではないという点です。関わったのは当時の一流の技術者たちでした。防げなかったのは能力ではなく、確認の仕組みがなかったからです。第1回で見た「関門は人の注意力に依存させない」という原則を、この事故は裏側から証明しています。

✅ 要点まとめ

  • テストが通ったのは「確認した範囲では期待通り」の意味。保証ではない
  • 3つの役割を区別する:回帰の検知・仕様の記述・設計の検査
  • テストピラミッド:単体を多く、E2Eを少なく。速く・安く・原因が分かるものを土台に
  • E2Eが中心(アイスクリームコーン)になると、遅く・壊れやすく・原因不明になる
  • 優先して守るのは過去の障害・境界値・異常系。均等に張ろうとしない
  • バグ修正は「失敗するテスト→修正→成功の確認→回帰の確認」の順で行う
  • カバレッジは危険の探索と監視に使う。目標値として追わない
  • テストしない判断も設計。壊れにくいものには張らない
  • テストしにくいコードは、境界が曖昧なサイン(第3回に戻る)

🚀 取り込み方:明日から使う3段階

今日(5分でできること):直近で自分が直した(または直す予定の)バグを1つ選び、そのバグを再現するテストを1本書いてみてください。まだ直っていないなら、失敗することを確認します(重要です)。直っているなら、バグを再現する入力を戻して、テストが失敗するかを確認します。失敗しないなら、テストがバグを捉えていません。

今週(小さく試す):次の新機能で、第2回の受け入れ条件をそのままテストに変換してみてください。「〜のとき、〜となる」の1行が、テスト1本に対応します。あわせて、異常系のテストを1本追加します(空の入力、権限なし、外部失敗など)。テストの本数より、境界と異常系を押さえられているかを意識します。

今月(定着させる):自分の担当範囲でテストの一覧を作り、役割(回帰/仕様/設計)と種類(単体/結合/E2E)で分類してみてください。偏りが見つかることが多いはずです(E2Eばかり、正常系ばかり、など)。あわせて、自分のテストの実行時間を計測してください。テスト全体が遅いと、実行が億劫になり、いずれ実行されなくなります(第7回のCIで自動化する前提は、ここにあります)。

🔥 ハマりポイント

その1:「テストが通ったから大丈夫」と言う
テストが通ったことは、確認した範囲の報告です。「テストが通っています」と伝えるのは正しいのですが、それを安心の保証として扱うと危険です。特に、確認していない範囲(新しい入力パターン、想定外の利用、外部の失敗)が残っていることを意識します。伝えるときは「テストではこの範囲を確認済みです」と範囲を添えるのが誠実です。

症状:テストは緑なのに本番で障害が出る。原因:確認範囲の外側で起きた。対処:テストの範囲を意識し、異常系を追加する。

その2:カバレッジを目標にする
「カバレッジ80%」を目標にすると、行を通すだけのテストが増えます。これは最も危険なテストです。なぜなら、何も検証していないのに、確認したように見えるからです。カバレッジは低い場所を探す道具であり、達成すべき目標ではありません

症状:カバレッジは高いが、障害が減らない。原因:検証を伴わないテスト。対処:テストの中身(アサーション)を確認する。異常系の有無を確認する。

その3:実装の詳細に依存したテストを書く
テストを「実装の中身」に合わせて書くと、リファクタリングのたびにテストが壊れます。たとえば、内部の関数呼び出し回数を確認するテストは、実装を変えると落ちます。すると、テストを直す時間が実装を直す時間より長くなり、やがてテストが無視されます。原則は、「観察できる振る舞い」に対してテストを書くことです。第3回の境界の外側(入出力)に対してテストを書けば、中身を変えてもテストは生き残ります。

症状:リファクタリングのたびに大量のテストが落ちる。原因:実装の詳細に依存している。対処:境界(入出力)に対するテストに書き換える。

その4:たまに落ちるテストを放置する
時々落ちるテスト(フレーキーなテスト)は、テスト全体の信用を破壊します。1つあると、「また落ちてるだけだよね」という判断が生まれ、本当の失敗が見逃されます(オオカミ少年的な状態)。原因は、実行順序への依存、時刻や乱数への依存、共有状態の不足、非同期処理の待ち不足などです。見つけたら、優先して直すか、一時的に無効化してチケット化します。放置は最も悪い選択です。

症状:「このテストは時々落ちるので無視しています」という会話が成立する。原因:フレーキーなテストの放置。対処:直すか、無効化して記録する。CIで不安定なテストを検知する。

🔄 代替技術との比較:テストの書き方と戦略

テストには複数の書き方・戦略があります。目的に応じて選ぶものであり、1つに統一する必要はありません。

戦略考え方向いている場面弱み
テスト駆動開発(TDD)実装の前にテストを書く。設計が固まる仕様が明確な機能。ロジックが複雑な箇所習得に時間がかかる。探索的な開発では過剰になりやすい
バグ駆動(再現テスト)バグ発見時に再現テストを書き、直す運用中のシステム。日々の不具合対応新機能の品質は保証しない
振る舞い駆動(BDD)利用者の言葉(〜のとき〜となる)でテストを書くチームと非エンジニアの認識合わせすべてをこの形式にすると冗長になる
プロパティベーステスト「常に成り立つ性質」を定義し、入力を自動生成して確認するアルゴリズム・変換処理。境界の想定漏れを防ぐ性質の定義が難しい。結果の再現が面倒な場合がある
スナップショットテスト出力を記録し、変化を検出する表示・生成物の回帰検知変更の意図と無関係な差分で落ちる。乱用すると負債

補足として、テストの置き場所にも選択肢があります。コードと同じリポジトリに置く(同一リポジトリ)のが現代的な標準です。理由は、実装とテストが同じ変更で更新されるためです(テストを別リポジトリに置くと、実装の変更とテストの更新がずれます)。また、テストの実行環境も選択肢があります。ローカル、CI(第7回)、専用のテスト環境。ローカルで速く回せるテストを土台に、CIで全体を確認するのが標準的な構成です。

📅 今後の展望:AIテスト生成と「検証の設計」

テストを巡る変化を3つ挙げます。第一に、AIによるテスト生成です。実装コードからテストを自動生成する技術は実用化が進んでいます。ただし、生成されたテストは実装の現在の動作を写し取る傾向があります。つまり、バグも一緒に固定してしまう危険があります。AI生成テストを活かすには、「何が正しいか」を与える必要があります——これが、第2回の受け入れ条件と、今回の境界値・異常系の観点です。AIは網を編むのが速い。どこに張るかを決めるのは人間という分担になります。

第二に、テストの自動実行の常態化です。第7回で扱うCIにより、変更のたびにテストが自動実行されるのが標準になりました。テストを書いても実行されなければ意味がありません。「テストがある」と「テストが守っている」は違うので、実行の仕組みとセットで考える必要があります。

第三に、本番環境での検証の広がりです。テスト環境では再現しにくい問題(実際のトラフィック、実際のデータ量)に対して、本番で小さく試して観測する手法(カナリアリリース、フィーチャーフラグ、A/Bテスト)が普及しています。これはテストの網を本番に広げるという考え方で、第7回と第8回で扱います。ただし原則は同じです。見つけるのが遅いほど高い。ならば、早く・小さく見つける場所を設計する

まとめ

この記事を読んだあなたは、次にテストを書くとき、「これは何を守る網か」を最初に考えるようになります。そして、バグを見つけたときには、直す前に再現テストを書くという手順を持てます。

テストは、あなたの実装を疑うための装置ではありません。未来の変更から、いまの正しさを守るための網です。網は、張れば張るほどよいものではありません。守る価値のある場所に、適切な粗さで張る——その判断ができることが、テスト設計の技術です。

第7回では、関門7の「リリース」を掘ります。テストが通った変更を、どうやって本番に出すのか。手作業のデプロイがなぜ危険なのか、自動化された流れ(CI/CD)は何を守るのか、そして戻せる出し方をどう設計するかを扱います。

参考文献

  1. Edsger W. Dijkstra, “Notes on Structured Programming”, 1970(テストと正しさに関する指摘の出典として広く引用) — https://www.cs.utexas.edu/users/EWD/
  2. Glenford J. Myers, “The Art of Software Testing” (3rd ed.), Wiley, 2011(テスト設計の古典) — https://www.wiley.com/
  3. Kent Beck, “Test-Driven Development: By Example”, Addison-Wesley, 2002(TDDの原典) — https://www.oreilly.com/
  4. Mike Cohn, “Succeeding with Agile”, Addison-Wesley, 2009(テストピラミッドの出典) — https://www.mountaingoatsoftware.com/books/succeeding-with-agile
  5. Martin Fowler, “The Practical Test Pyramid” — https://martinfowler.com/articles/practical-test-pyramid.html
  6. Martin Fowler, “Mocks Aren’t Stubs”(テストの二重化と実装依存の問題) — https://martinfowler.com/articles/mocksArentStubs.html
  7. Google Testing Blog, “Test Sizes”(テストの分類と実行時間の考え方) — https://testing.googleblog.com/2010/12/test-sizes.html
  8. Kent C. Dodds, “The Testing Trophy and Testing Classifications” — https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications
  9. NASA, “Mars Climate Orbiter Mishap Investigation Board Phase I Report”, 1999(単位取り違え事故の公式報告) — https://llis.nasa.gov/llis_lib/pdf/1009464main1_0641-mr.pdf
  10. Koen Claessen, John Hughes, “QuickCheck: A Lightweight Tool for Random Testing of Haskell Programs”, ICFP 2000(プロパティベーステストの原典) — https://dl.acm.org/doi/10.1145/351240.351266
  11. Yue Jia, Mark Harman, “An Analysis and Survey of the Development of Mutation Testing”, IEEE TSE, 2011(テストの質を測る手法) — https://ieeexplore.ieee.org/document/5487526
  12. ISO/IEC 25010:2011, “Systems and software Quality Requirements and Evaluation (SQuaRE) — Quality models” — https://www.iso.org/standard/35733.html
  13. ISTQB, “Certified Tester Foundation Level Syllabus”(テストの標準的な知識体系) — https://www.istqb.org/
  14. OWASP, “Web Security Testing Guide”(セキュリティ観点のテスト) — https://owasp.org/www-project-web-security-testing-guide/
  15. Google, “Google’s Engineering Practices documentation”(テストとレビューの関係) — https://google.github.io/eng-practices/
  16. 情報処理推進機構(IPA), 「ソフトウェア開発データ白書」(不具合の作り込み・流出に関する統計) — https://www.ipa.go.jp/
ジュニアエンジニア向け 開発フロー入門 ── 全8回の一覧
いま読んでいるのは 第6回 です。読みたい回から始めても構いません。
  1. 第1回:全体地図:1つの変更は8つの関門を通る
  2. 第2回:仕事の作り方:「いい感じに直して」をそのまま実装しない
  3. 第3回:設計の粒度:実装前に決めること、決めないこと
  4. 第4回:GitとPR:コミットは未来の自分への手紙
  5. 第5回:コードレビュー:指摘ではなく共有として行う
  6. ▶ 第6回:テスト:テストは仕様書であり、網である(この記事)
  7. 第7回:CI/CDとリリース:「手元では動く」を卒業する
  8. 第8回:運用と障害対応:壊れた最初の10分で何をするか

© Copyright 2005-2026| Rui Software | All Rights Reserved