テストとコード品質

良いユニットテストの条件:FIRST原則5つを総まとめ

テストコードを大量に書いたのに肝心のバグは見つからず、機能を1つ直すたびにテストが次々と壊れた経験はありませんか?

読了 5 分
良いユニットテストの条件:FIRST原則5つを総まとめのカバー画像

テストコードを大量に書いたのに肝心のバグは見つからず、機能を1つ直すたびにテストが次々と壊れた経験はありませんか?

結論から言うと、良いユニットテストは数やカバレッジではなく、「FIRST原則」を守れているかで決まります。FIRSTはFast(高速)、Isolated(独立)、Repeatable(再現可能)、Self-validating(自己検証)、Timely(適時)の頭文字です。

この5つだけ覚えておけば、テストは負担ではなく頼れる安全網になります。今日は私の経験も交えながら、1つずつ見ていきます。

FIRST原則をひと目で整理すると?

FIRSTは、ロバート・マーティン(アンクル・ボブ)が著書『Clean Code』で紹介したことで広く知られるようになった原則です。

それぞれの文字に、良いユニットテストの条件が1つずつ込められています。

文字 意味 ポイント
F Fast 数百件を数秒以内に
I Isolated テスト同士が互いに干渉しない
R Repeatable いつどこで実行しても同じ結果
S Self-validating 成功/失敗を自動判定
T Timely 適切なタイミングで書く

1つずつ詳しく見ていきましょう。


Fast:なぜこれほど速くする必要があるのか?

ユニットテストは速くなければなりません。本当に。

遅いと開発者は実行しなくなるからです。1回の実行に3分かかるテストスイートは、やがてコミット前にかろうじて実行するだけになります。

コードを直すたびに、負担なく数百件を繰り返し実行できてこそ、テストは役割を果たします。

遅くなる最大の原因は、実際のDB、ネットワーク、ファイルシステムへのアクセスです。こうした外部依存性はテストダブル(Mock、Stub)に置き換えるのが定石です。

1件あたり数ミリ秒、全体でも数百件を数秒以内に終えることを目標にするとよいでしょう。

テストが全部グリーンになったときの、この安心感。分かりますよね?
テストが全部グリーンになったときの、この安心感。分かりますよね?

Isolated:テスト同士が干渉してはいけない理由

各テストは独立していなければなりません。別のテストの結果に依存してはいけません。

たとえばAテストが作ったデータをBテストが使うと、Aが失敗した瞬間にBも巻き込まれて失敗します。実行順序が変わるだけで結果も変わります。

そのため、各テストは自分が使うデータを自分で用意し、終わったらきれいに片付ける必要があります。

以下は、各テストの前に状態を初期化する一般的なパターンです。

struct CalculatorTests {
    let calculator: Calculator

    init() {
        // テストごとに新しいインスタンスを生成 → 状態の共有を防止 suite 
        calculator = Calculator()
    }
}

こうすれば、どのテストが先に実行されても互いに影響しません。

Xcodeで@Testとinit()を使って書いたSwift Testingのテストスイートと成功結果の画面
init()により、各テストが新しいインスタンスで始まり、互いに独立します

Repeatable & Self-validating:結果が揺らいではいけない

まずR、再現可能性から見ていきます。テストは何度実行しても、どの環境で実行しても同じ結果になるべきです。

今日通ったのに明日は失敗するテストを「flakyテスト」と呼びます。信頼を壊す大きな原因です。

現在時刻やランダム値に依存すると、この問題が起きやすくなります。時間を扱う必要があるなら、固定値を注入して制御するのが安全です。

S、自己検証も重要です。テストは成功か失敗かを自分で判断できなければなりません。

コンソールに値を出力し、人が目で確認するのはテストではありません。#expectのようなアサーションで結果を自動判定させる必要があります。

良いテストが示すのはグリーンかレッドのどちらか一方だけです。人に「これで合っている?」と考えさせるなら、そのテストはすでに失敗です。


Timely:テストはいつ書くべきか?

最後のTは適時性です。適切なタイミングでテストを書くという意味です。

TDD(Test-Driven Development、テスト駆動開発)では、プロダクションコードより先にテストを書きます。その方法を厳密に採用しなくても、機能実装に近いタイミングで一緒に書くことが重要です。

機能を完成させて数週間後にテストをまとめて書こうとすると、その頃にはテストしにくい構造に固まっていることが多いからです。

テストを後回しにしないこと。それがTimelyの核心です。


まとめ

FIRSTは暗記するためのルールではなく、テストに息苦しさを感じたとき、どこがずれているかを示してくれるチェックリストに近いものです。

遅くないか?互いに絡み合っていないか?結果が揺れていないか?人の確認が必要ではないか?書くのが遅すぎないか?この5点だけでも確認すれば、テストの質は大きく向上します。

今日から、テストを1つ書くたびにFIRSTを少し思い出してみてください。きっと頼れる仲間になってくれます。

あわせて読みたい