失敗経路と副作用を分離してテストする:設計の考え方
一言でいうと
障害テストでは、最終的な戻り値とあわせて、呼び出し回数・待機・ファイルの状態まで観察します。
なぜ必要なのか
一瞬切れた接続はリトライで復旧できますが、誤った入力は同じリクエストを繰り返しても 直りません。2つのエラーをどちらもexcept Exceptionでまとめると、成功レスポンスが返っても、 隠れた重複呼び出しと不要な待機が生じます。実際の外部サーバーを切断したり、数秒ずつ待ったり しなくても失敗経路をテストできてこそ、開発者は毎回テストを実行します。
どう動くのか
fetch(send, sleep, cache)は、引数として受け取ったsendを呼び出し、TimeoutErrorの場合にだけリトライします。 呼び出しは最大3回で、1回目の失敗の後に0.1秒、2回目の失敗の後に0.2秒をsleepに渡します。 3回目の失敗ではそれ以上待たずに例外を上げます。ValueErrorのような恒久的なエラーは、 ただちに伝播させます。成功した文字列は、pathlib.Pathであるcacheに、UTF-8で保存してから返します。 保存自体が失敗しても、エラーを隠しません。
성공 → 파일 저장 → 반환
시간 초과 → 시도 남음? → 대기 → 재호출
└─ 없음 → 예외, 기존 파일 그대로
영구 오류 → 즉시 예외
現場での姿
偽のsendは、呼び出しの記録をリストに残し、決められた順序でエラーと値を返します。 偽のsleepは、遅延の値を記録するだけです。tmp_pathはテストごとに別のディレクトリを提供するので、 前のテストのキャッシュが次のテストを成功させることはありません。既存のキャッシュを保持する契約は、 ネットワークの失敗に対するものです。単純なwrite_textは、書き込み中のディスク障害まで原子的に 保護するわけではないので、この実装をアトミックなファイル置き換えとは呼びません。
次のラボですること
成功、一時的な失敗からの復旧、リトライの使い切り、恒久的なエラー、待機時間の数列、成功時の保存、失敗時の保持、保存エラーを、 それぞれ検証します。root環境では、chmodだけで書き込み失敗を作れると仮定してはいけません。 ディレクトリにファイルの内容を書き込ませるなど、環境への依存が小さい失敗条件を選びます。 採点は同じテストを正常な実装と欠陥のある実装で実行し、レポートの文言やモックの呼び出しの宣言だけでは成功になりません。
参考: pytest tmp_path