自動テスト
自動テストとは
自動テストは、「コードが期待どおり動くか」をプログラムで検証する手法です。一度書けば何度でも一瞬で実行でき、変更のたびに壊れていないかを確かめられます。
import { describe, expect, it } from 'vitest'
import { searchArticles } from './search'
describe('searchArticles', () => {
it('キーワードに一致する記事だけを返す', () => {
const articles = [{ id: 'docker', title: 'Docker', tags: ['コンテナ'] }]
const result = searchArticles(articles, 'コンテナ')
expect(result.map((a) => a.id)).toContain('docker')
})
})
テストの種類(テストピラミッド)
| 種類 | 対象 | 特徴 |
|---|---|---|
| ユニットテスト | 関数・クラス単体 | 速い・多く書く。土台 |
| 統合テスト | 複数モジュールの連携 | コンポーネント+データ層など |
| E2Eテスト | アプリ全体をブラウザ操作 | 遅いが本物に近い。少数精鋭 |
「ピラミッド」と呼ぶのは、下ほど多く、上ほど少なくするのが目安だからです。E2Eテストは本物に近い代わりに1本あたり数秒〜数十秒かかり、ボタンの位置が変わっただけで赤くなります。上を厚くすると実行に何十分もかかり、落ちても原因の箇所がすぐには分からないテスト群ができあがります。
1つのテストの形
テストの中身は、言語やフレームワークが変わっても同じ3段構えです。
| 段階 | やること |
|---|---|
| 準備(Arrange) | 入力データやテスト対象の状態を用意する |
| 実行(Act) | テストしたい処理を1回だけ呼ぶ |
| 検証(Assert) | 結果が期待どおりかを確かめる |
it('1000円以上の注文は送料が無料になる', () => {
const order = { subtotal: 1000 } // 準備
const fee = shippingFee(order) // 実行
expect(fee).toBe(0) // 検証
})
テスト名は「何をしたらどうなるか」を文で書きます。test1 や shippingFee のテスト では、失敗レポートを見ても何が壊れたのか分かりません。テストが落ちた瞬間に読むのは、コードではなくこの名前です。
何をテストするか — 境界値と異常系
すべての入力は試せません。効くのは、仕様が切り替わる境目と、うまくいかない道です。
「1000円以上で送料無料」なら、試すのは999円・1000円・1001円の3点です。500円と800円をいくら足しても、> と >= の書き間違いは見つかりません。バグは境目に集まります。
異常系も同じくらい重要です。0件のリスト、空文字、想定外に大きい値 — 正常系だけのテストは「うまくいくときはうまくいく」ことしか証明しません。実際の障害の多くは、誰も試していない道で起きます。
カバレッジは目安であって目標ではない
カバレッジ(網羅率)は、テストの実行でコードの何%が通ったかを示す数値です。
ただし、通ったことと検証したことは別です。
it('動く', () => {
calculateTotal(items) // 呼んだだけ。結果を確かめていない
})
これでも calculateTotal の行カバレッジは上がります。カバレッジ100%でもバグは残り、数値を目標に掲げると意味のないテストを量産する動機が生まれます。薄い場所を見つける道具として使い、達成すべき数字としては扱いません。
初学者向けポイント
- テストの真価は「変更への保険」。リファクタリングしても壊れていないと即わかる安心感が開発速度を上げる
- [[TypeScript]] の型チェックとテストは補完関係(型は「形」を、テストは「振る舞い」を守る)
- [[React]] のテストは Testing Library で「ユーザーから見える振る舞い」を検証するのが主流
- [[Vite]] と設定を共有できる Vitest が現在のJS界の定番ランナー
- 外部APIやDB・現在時刻に依存する処理は [[テストダブル(モック・スタブ)]] で差し替える — ユニットと統合の境目はここで決まる
- バグを直すときは、まずそのバグを再現する失敗テストを足してから直す。同じ壊れ方が二度と戻ってこない
ここから先を選ぶ地図
テストは書き方だけでなく、書く順序・回す仕組み・測れないものの扱いまで広がります。近い群から選んでください。
書く順序を変えて設計に効かせる
テストを実装より先に書くと、テストは検証だけでなく設計の道具になります。そのサイクルが [[テスト駆動開発]]、テストを安全網として内部構造を直し続けるのが [[リファクタリング]]、そこで目指すコードの姿を示すのが [[クリーンコード]] です。「テストが書きにくい」と感じたら、それは設計からの信号です。
依存と経路をどう切り分けるか
現実のコードは外部APIやDB、現在時刻に依存します。それを代役に差し替えてユニットテストを成立させるのが [[テストダブル(モック・スタブ)]]。[[フィーチャーフラグ]] を増やすと通る経路が倍々になるため、どこまで組み合わせを試すかの線引きが要ります。
自動で回し続ける
テストは実行され続けて初めて安全網になります。pushのたびに走らせるのが [[CI/CD]]、「通らないとマージできない」という関門やジョブの並行制御を扱うのが [[CI/CDパイプラインのゲートと並行制御]]。機械が見るテストと人が見る [[コードレビュー]] は、一方だけでは埋まらない穴を互いに補います。
白黒がつかないものを測る
出力が毎回変わる機能には、完全一致の期待値を書けません。評価セットを作り合格率のしきい値で回帰を検知するのが [[LLMアプリの評価とテスト]]、土台となる指標の考え方が [[モデル評価と指標]] です。
チームの営みに載せる
短いサイクルで作って確かめる [[アジャイル開発]] は、壊れていないことを毎回確認できる前提の上に成り立ちます。[[AIコーディング支援]] が書いたコードも同じで、「動くように見える」と「動く」を分けるのはテストの役目です。
関連技術とのつながり
- [[CI/CD]] — pushのたびにテストを自動実行する仕組み
- [[TypeScript]] — 型とテストの二段構えで品質を守る
- [[React]] / [[Vite]] — テスト環境(Vitest + Testing Library)との統合
- [[コードレビュー]] — マージ前のもう1つの品質ゲート。機械が見るテストと人が見るレビューは補い合う
- [[テストダブル(モック・スタブ)]] — 依存を差し替えて単体で検証するための技法
- [[LLMアプリの評価とテスト]] — 出力が毎回変わる機能の回帰をどう検知するか
Q: テストピラミッドで、E2Eテストを厚くしすぎると起きることとして本文が挙げているのはどれ?
- [ ] ユニットテストが自動的に実行されなくなる
- [x] 実行に何十分もかかり、落ちても原因の箇所がすぐに分からなくなる
- [ ] テストの本数を数えられなくなる
解説: E2Eは1本あたり数秒〜数十秒かかり、些細な変更でも壊れます。下を厚く上を薄くするのが目安です。
Q: 「1000円以上で送料無料」という仕様をテストするとき、本文が勧める入力はどれ?
- [ ] 500円と800円のように、無料にならない値を多く並べる
- [x] 999円・1000円・1001円という境目の3点
- [ ] 実際の注文データを1件だけそのまま使う
解説: バグは仕様が切り替わる境目に集まります。境目を外した値をいくつ足しても `>` と `>=` の書き間違いは見つかりません。
Q: カバレッジについての説明として本文の趣旨に合うのはどれ?
- [ ] 100%にすればバグが残らないことを保証できる
- [ ] 数値が高いほどテストの検証内容も必ず充実している
- [x] 手薄な場所を探す道具であり、達成すべき目標として扱うものではない
解説: 処理を呼ぶだけで結果を確かめないテストでもカバレッジは上がります。数値を目標にすると意味のないテストを量産する動機が生まれます。