45395 - シコウサクゴ -

AIと一緒に7,484テストを積み上げた:本番稼働システムのテスト戦略

2026-04-03
AI駆動開発
AI駆動開発
Claude Code
テスト
pytest
品質管理
Last updated:2026-04-05
10 Minutes
1974 Words

テストがゼロの状態から7,484テストを積み上げるのに約4ヶ月かかりました。そのうち約80%はClaude Codeが書きました。

しかし「AIにテストを書かせる」のは簡単ではありません。何も指示しないと、「通るだけで意味のないテスト」が量産されます。本記事では、AIと協働してテストを積み上げる際の戦略と、実際に効いた指示の出し方を記録します。


テストゼロからのスタート

初期状態

1
テスト数: 0
2
カバレッジ: 0%
3
コード規模: 約15,000行
4
本番稼働: launchdで24時間自動実行中

「テストがないのに本番で動いている」状態でした。怖くてコードを変更できません。変更すると何が壊れるかわかりませんでした。

テストがない理由

  1. 個人開発で「自分がわかっているから大丈夫」 — 3ヶ月後の自分は他人だった
  2. 本番が動いているからテスト不要 — 動いていることと正しいことは別
  3. テストを書く時間がない — テストなしの修正で発生するバグ対応の方が時間がかかる

テスト追加の3フェーズ

Phase 1: クリティカルパスのみ(0→500テスト)

本番で最も壊れたら困るモジュールから始めました。

1
優先度の判断基準:
2
1. 本番で毎日実行されるコード(最優先)
3
2. 重要な処理を担うコード(データ更新・外部連携)
4
3. データパイプライン(分析 → シグナル生成)
5
4. ユーティリティ・共通モジュール(後回し)

AIへの依頼:

1
dataProcessor.py の calculate_allocation メソッドのテストを書いてください。
2
3
制約:
4
- pytest形式
5
- 正常系3ケース、異常系3ケース、境界値2ケースを含む
6
- 実際のAPI呼び出しはモック化
7
- テスト名は test_calculate_allocation_XXX 形式

Phase 2: 回帰テスト(500→3,000テスト)

リファクタリングや新機能追加のたびに、変更対象のモジュールのテストを追加しました。

1
ルール: コードを変更する前にテストを書く(TDD的アプローチ)
2
3
1. 変更対象のモジュールの現在の動作をテストで記録
4
2. テストがPASSすることを確認
5
3. コードを変更
6
4. テストが引き続きPASSすることを確認
7
5. 新しい機能のテストを追加

Phase 3: カバレッジ駆動(3,000→7,484テスト)

カバレッジ計測を導入し、未テストの行を特定して埋めていきました。

Terminal window
1
# カバレッジ計測
2
pytest --cov=_dataProcessingEngine --cov-report=html tests/
3
4
# カバレッジ閾値の強制(pytest.ini)
5
[tool:pytest]
6
addopts = --cov-fail-under=80

--cov-fail-under=80により、カバレッジが80%を下回るとCIが失敗します。新しいコードを追加するたびにテストも追加することを強制する仕組みです。


テスト件数の内訳

エンジンテスト数主な対象
データ処理エンジンA4,150+スコアリング・データ更新・回帰テスト・リスク管理
データ処理エンジンB2,064+データ項目選定・統計的最適値・スケジュールキャンセル
データ処理エンジンC1,270+25項目スコアリング・メトリクス連動・イベント後ドリフト分析
合計7,484+

AIにテストを書かせるコツ

1. 「通るだけのテスト」を防ぐ

1
# ❌ AIが書きがちな「通るだけのテスト」
2
def test_calculate_score():
3
result = calculate_score(data)
4
assert result is not None # これは何も検証していない
5
6
# ✅ 具体的な期待値を指定させる
7
def test_calculate_score_high_priority_signal():
8
data = create_test_data(metric_a=25, trend_cross="positive")
9
result = calculate_score(data)
10
assert result >= 1.5, "メトリクスA低値 + トレンド正転はスコア1.5以上"

対策: プロンプトに「assert is not None は禁止。具体的な期待値を検証すること」と明記する。

2. 境界値テストを明示的に要求する

AIは正常系のテストは得意ですが、境界値を自発的にテストしないことがあります。

1
プロンプト:
2
以下の境界値テストを含めてください:
3
- min_score の閾値ちょうど(1.9)と閾値未満(1.89)と閾値超(1.91)
4
- 処理数が max_concurrent と一致する場合
5
- 残リソースがゼロの場合
6
- 成功率が0%と100%の場合

3. テストデータの生成をファクトリ関数にする

1
# ❌ テストごとにデータを直書き
2
def test_case_1():
3
data = {"metric_a": 25, "trend": 0.5, "threshold_cross": True, "strength": 30, ...}
4
5
# ✅ ファクトリ関数を使う
6
def create_signal_data(
7
metric_a: float = 50.0,
8
trend: float = 0.0,
9
threshold_cross: bool = False,
10
strength: float = 20.0,
11
**overrides
12
) -> dict:
13
base = {"metric_a": metric_a, "trend": trend, "threshold_cross": threshold_cross, "strength": strength}
14
base.update(overrides)
15
return base
4 collapsed lines
16
17
def test_high_priority_signal():
18
data = create_signal_data(metric_a=25, trend=0.5)
19
# テストの意図が明確

AIに「テストデータ生成用のファクトリ関数を先に作成してから、テストを書いてください」と指示します。

4. conftest.py でフィクスチャを共有する

tests/conftest.py
1
import pytest
2
3
@pytest.fixture
4
def sample_config():
5
return {
6
"min_total_score": 1.9,
7
"lower_bound_multiplier": 2.0,
8
"upper_bound_multiplier": 3.0,
9
"max_concurrent": 3,
10
}
11
12
@pytest.fixture
13
def mock_api_client(mocker):
14
client = mocker.MagicMock()
2 collapsed lines
15
client.get_active_items.return_value = []
16
return client

「conftest.pyに共通フィクスチャを追加してから、テストを書いてください」と指示します。


テストが実際にバグを防いだ例

例1: スコア計算の不整合

リファクタリングで共通モジュールを変更した際、9データソースのうち3ソースでスコア計算結果が変わりました。テストがなければ本番で気づくまで発覚しませんでした。

1
テスト失敗メッセージ:
2
FAILED test_score_source_a - assert 2.3 == 2.8
3
スコアが0.5低下。VOLUME指標が計算から除外されている。

例2: 統計的最適値の境界値バグ

成功率100%のとき、最適比率が無限大になるバグです。テストがなければ「全リソースを1処理に投入する」事故が起きていました。

1
def test_optimal_ratio_success_rate_100_percent():
2
ratio = calculate_optimal_ratio(success_rate=1.0, rr_ratio=2.0)
3
assert ratio <= 1.0, "最適比率は100%を超えてはならない"

例3: 日付変換のタイムゾーンバグ

UTC→JST変換で9時間のオフセットが二重に適用されるバグです。イベント日フィルターが18時間ずれて動作する問題を検出しました。


テストの実行時間管理

7,484テストの実行時間は管理しないと膨大になります。

1
データ処理エンジンA: 4,150テスト → 約90秒
2
データ処理エンジンB: 2,064テスト → 約45秒
3
データ処理エンジンC: 1,270テスト → 約30秒
4
合計: 7,484テスト → 約165秒(3分弱)

高速化のための工夫

  1. 外部APIの完全モック化: 実際のAPI呼び出しを含むテストはtests/integration/に分離し、通常のCIでは実行しない
  2. --no-covオプション: カバレッジ計測を省略すると約30%高速化
  3. -xオプション: 最初の失敗で停止。全テスト実行は不要な局面で使用
  4. -kキーワードフィルタ: 変更した機能のテストのみ実行
Terminal window
1
# 開発中:変更箇所のテストのみ高速実行
2
pytest tests/ -k "optimal_ratio" -x --no-cov -q
3
4
# CI:全テスト実行(カバレッジ付き)
5
pytest tests/ --cov --cov-fail-under=80

学んだこと

1. テストゼロからでも始められる

「テストが1本もない」状態は絶望的に見えますが、1日10テスト追加すれば1ヶ月で300テストになります。AIに書かせれば1日50テストも可能です。

2. 「AIが書いたテスト」は人間がレビューする

AIは「テスト名は正しいが期待値が間違っている」テストを書くことがあります。特にドメイン固有のロジック(「最適比率が1.0を超えてはならない」など)は、人間の知識でレビューします。

3. カバレッジ閾値の強制が文化を作る

--cov-fail-under=80をCIに入れた瞬間から、「テストを書かない選択肢」がなくなりました。強制力のある仕組みが文化を変えます。

4. テスト実行時間は3分以内に保つ

テスト実行が遅いと「テストを実行しない」選択が増えます。3分以内なら「コミット前にとりあえず回す」習慣が維持できます。


まとめ

テスト戦略で重要なのは以下の3点です。

  1. クリティカルパスから着手: 本番で毎日動くコード → 重要な処理を担うコード → データパイプラインの順に優先
  2. AIへの具体的な指示: 「assert is not None禁止」「境界値を含める」「ファクトリ関数を先に作る」
  3. カバレッジ閾値の強制: --cov-fail-under=80でCIに組み込み、テストなしのコードを許さない

7,484テストの80%はAIが書きました。しかし「何をテストするか」を決めるのは人間の仕事です。

Article title:AIと一緒に7,484テストを積み上げた:本番稼働システムのテスト戦略
Article author:45395
Release time:2026-04-03

記事へのご質問・ご感想をお聞かせください

フィードバックを送る