ふくふくHukuhuku Inc.
EP.08Data Fetch 10分公開: 2026-09-02

取得コードをテストする ── 本物のAPIを叩かずに壊れを見つける

テストのたびに本物のAPIを叩くと、遅く、不安定で、相手にも迷惑です。かといってすべて作り物にすると、実物とずれたことに気づけません。その折り合いの付け方です。

#テスト#Python#API
執筆 / 監修
松尾 亮合同会社ふくふく 代表社員

データ基盤・データパイプライン構築 / BI / 生成 AI 活用支援を専門とするエンジニア (28 年)。 本記事は AI 利用ポリシーに基づき、生成 AI の補助で執筆 → 人間が監修・編集して公開しています。

プロフィール詳細
シェア

取得コードのテストは書きにくい部類に入ります。相手が外にいるので、こちらの都合で状態を作れません。ネットワークが要るので遅く、相手が落ちていればテストも落ちます。テストが不安定だと、誰も結果を見なくなります

かといって、応答をすべて手書きの作り物に置き換えると、今度は実物とのずれに気づけません。作り物は自分の思い込みどおりに動くので、思い込みが間違っていた場合のテストにならないのです。この回では、その折り合いをどう付けるかを扱います。

何を確かめたいのかを分ける

取得処理のテストは、確かめたいものが少なくとも3つに分かれます。ここを混ぜると、全部を1つのテストでやろうとして無理が出ます。応答の解釈が正しいかを見たいだけなのに、そのために本物の相手が必要になり、相手が落ちていると解釈のテストまで落ちる。原因の切り分けもできなくなります。

取得処理のテストで確かめたいこと
確かめたいこと本物のAPIが要るか手段
応答を正しく解釈できるか不要記録した応答で
失敗時に正しく振る舞うか不要で失敗を作る
相手の仕様が変わっていないか必要を低頻度で

上2つは本物を叩かずにできます。毎回のテストで叩く必要があるのは3つ目だけで、これは1日1回など低い頻度で構いません。開発中に何度も実行するテストと、仕様変更を見張るテストは、目的も適切な頻度も違います。混ぜると、遅くて不安定なテストが1つできあがるだけです。

本物の応答を一度記録して、使い回す

解釈のテストには、本物の応答を一度だけ取得して保存し、以降はそれを読む方式が向きます。と呼ばれる手法です。手書きの作り物と違い、実際に返ってきたものを使うので、思い込みが混ざりません。

保存した応答は としてリポジトリに置きます。ここで注意が要るのは、応答に含まれる個人情報や鍵をそのまま置かないことです。保存する前に該当する項目を伏せ字にします。一度コミットすると履歴から消えないので、置く前に確認してください。

記録する応答は、うまくいったものだけにしないでください。空の結果、1件だけの結果、項目が欠けている結果。こうした端の条件こそが壊れやすい場所です。実際に返ってきた変わった応答を見つけたら、それも記録に加えておくと、同じ壊れ方を二度としなくなります。

記録した応答を読んで解釈だけをテストする
Python
import jsonfrom pathlib import Path
FIXTURES = Path(__file__).parent / "fixtures"

def load_fixture(name: str):    return json.loads((FIXTURES / f"{name}.json").read_text(encoding="utf-8"))

def test_parse_items():    """本物から一度だけ取って保存した応答を使う。ネットワークは要らない。"""    payload = load_fixture("items_page1")    items = parse_items(payload)
    assert len(items) == 100    assert items[0]["id"] == "A-0001"    # 数値として解釈できているか(文字列のままになっていないか)    assert isinstance(items[0]["price"], (int, float))    # 欠損が None になっているか("" や "null" のままでないか)    assert items[3]["name"] is None

失敗の側をテストする

取得処理で本当に壊れるのは、成功したときではなく失敗したときです。429 が返ったら待つのか、500 なら何回やり直すのか、途中で切れたらどうなるのか。これらは本物の相手では再現できないので、作り物で状況を作ります。相手にわざと 500 を返してもらうことはできません。

429 を返す相手を作って、待ってからやり直すことを確かめる
Python
class FakeResponse:    def __init__(self, status_code, headers=None, payload=None):        self.status_code = status_code        self.headers = headers or {}        self._payload = payload or {}
    def json(self):        return self._payload
    def raise_for_status(self):        if self.status_code >= 400:            raise RuntimeError(f"HTTP {self.status_code}")

def test_retries_on_429(monkeypatch):    """1回目は429、2回目は成功。待ち時間を計測して、待っていることを確かめる。"""    calls = []    slept = []
    def fake_get(url, **kwargs):        calls.append(url)        if len(calls) == 1:            return FakeResponse(429, headers={"Retry-After": "2"})        return FakeResponse(200, payload={"items": []})
    monkeypatch.setattr("mymodule.requests.get", fake_get)    monkeypatch.setattr("mymodule.time.sleep", lambda s: slept.append(s))
    fetch_with_retry("https://example.com/api")
    assert len(calls) == 2       # 1回やり直している    assert slept == [2.0]        # Retry-After に従って待っている
待ち時間は実際に待たない

テストで本当に 2 秒待つと、テストが遅くなります。上のように待つ処理を差し替えて、待った長さだけを記録すれば、一瞬で検証できます。

相手が変わったことに気づく

記録した応答だけでテストしていると、相手の仕様が変わったことに永遠に気づけません。列が増えた、型が変わった、項目名が変わった。こうしたは、実際に叩いてみないと分かりません。

そこで、本物を叩くテストを別に用意し、低い頻度で回します。中身の値は変わるので、値ではなく形だけを確かめるのがこつです。件数がゼロでないか、必要な項目が存在するか、型が想定どおりか。

  • 通常のテストとは分けて動かす — 失敗しても普段の開発を止めない
  • 値ではなく形を見る — 「価格が 1200 円」ではなく「価格が数値である」
  • 失敗したら通知する — 気づかれない検査は無いのと同じ

本物を叩くテストは、結果が安定しないことがあります。相手が一時的に落ちていた、たまたま結果がゼロ件だった、といった理由で失敗すると、になります。そうなると誰も結果を信じなくなるので、失敗したら即座に赤にするのではなく、数回続いたら通知するくらいの緩さで運用するほうが実際的です。

この形のテストと、記録した応答の更新をセットにしておくと、仕様変更に気づいたときの直しが早くなります。テストの設計そのものは LLM時代のテスト戦略 でも扱っています。次回からは、Python の道具の話に移ります。

シェア

この記事の感想を教えてください

あなたの 1 クリックで、本当にこの記事は更新されます。「もっと詳しく」「続編希望」が一定数集まった記事は、 ふくふくが 実際に内容を拡充したり続編記事を公開 します。 送信したリアクションはお使いのブラウザに記録され、再カウントされません。

シリーズの外も探す:

まずは、現状を聞かせてください。

要件が固まっていなくて大丈夫です。現状診断と方針提案までを無料でお手伝いします。

無料相談フォームへ hello [at] hukuhuku [dot] co [dot] jp