失敗経路と副作用を分離してテストする
目標
ネットワークも実際の待機も使わずに、呼び出し回数・バックオフ・ファイルの保持・保存の失敗を再現します。
なぜ重要なのか
正常なリクエストが1回成功したからといって、境界値や障害からの復旧が保証されるわけではありません。このラボでは、各関数の契約を小さく実装またはテストしたうえで、実際の実行につなげます。コードが存在するかどうかやレポートの文言だけを見るのではなく、結果・例外・保存状態を検査します。前のステップのコードを維持したまま、次のステップへ進んでください。
ステップ
/root/work/test-failures-lab/test_service.pyで、fetchがsendの文字列をそのまま返し、sendをちょうど1回だけ呼び出すことをテストしてください。cacheはtmp_pathの下のpathlib.Pathとして渡します。最初の準備は次のコマンドで行います。
mkdir -p /root/work/test-failures-lab
cp /opt/fixtures/practice_depth/test-failures-lab/* /root/work/test-failures-lab/
cd /root/work/test-failures-lab
/root/work/test-failures-lab/test_service.pyで、最初のsendはTimeoutErrorを、2回目は'fresh'を返すように構成し、fetchが'fresh'を返すことを確認してください。/root/work/test-failures-lab/test_service.pyで、常にTimeoutErrorになるsendを渡し、合計3回の呼び出しのあとでTimeoutErrorが伝播することを確認してください。/root/work/test-failures-lab/test_service.pyで、ValueErrorを発生させるsendがちょうど1回だけ呼び出され、同じ種類の例外が伝播することをテストしてください。/root/work/test-failures-lab/test_service.pyで、すべてのsendがTimeoutErrorのとき、sleepに渡された値がちょうど[0.1, 0.2]であることを検査してください。実際にはスリープしません。/root/work/test-failures-lab/test_service.pyで、ハングルの文字列で成功させたあと、cacheファイルをUTF-8で読み込み、同じ内容が保存されていることを確認してください。/root/work/test-failures-lab/test_service.pyで、cacheに'old'をあらかじめ書き込んでから、sendを失敗させ続けてください。例外の後も、ファイルの内容が'old'のままであることを確認します。/root/work/test-failures-lab/test_service.pyで、sendが成功しても、cacheへの書き込みが失敗したらOSErrorが伝播することをテストしてください。cacheにtmp_pathのディレクトリそのものを渡すと、決定的に書き込みが失敗します。
参考
- パッケージはイメージにインストールされています。インターネット接続やpip installは必要ありません。
- 準備のコピーは最初の1回だけ行ってください。もう一度コピーすると、作業ファイルが初期化されます。
/root/work/test-failures-lab/service.pyは、最初の準備でコピーしたローカル実行用の提供コードです。修正したり、例のコードで上書きしたりせず、test_service.pyだけを作成してください。- テストは
from service import ...で提供関数を読み込んでください。採点は、別の一時ディレクトリにテストだけをコピーし、正常な実装と欠陥のある実装で実行します。ステップごとに、実際のテスト実行中にその欠陥を検出する必要があり、正常なコードでは、実行したテストがすべて成功しなければなりません。収集エラー・インポートエラー・強制終了は検出として認められません。外部ファイルやネットワークに頼らないでください。 - 各ステップの採点は45秒以内に実行されます。無限ループや実際の待機を作らないでください。
成功経路の不要な呼び出しを検出する
/root/work/test-failures-lab/test_service.pyで、fetchがsendの文字列をそのまま返し、sendをちょうど1回だけ呼び出すことをテストしてください。cacheはtmp_pathの下のpathlib.Pathとして渡します。最初の準備は次のコマンドで行います。
mkdir -p /root/work/test-failures-lab
cp /opt/fixtures/practice_depth/test-failures-lab/* /root/work/test-failures-lab/
cd /root/work/test-failures-lab
callsリストに記録する関数を注入してください。戻り値だけを見ていると、重複した呼び出しを見逃します。
採点はbash /opt/lab/checks/test-failures-lab/01-contract.shで直接再現できます。ファイルを保存してから、もう一度実行してください。
1回失敗させたあとで復旧させる
/root/work/test-failures-lab/test_service.pyで、最初のsendはTimeoutErrorを、2回目は'fresh'を返すように構成し、fetchが'fresh'を返すことを確認してください。
iteratorまたは呼び出し回数で順序を制御します。テスト自体では、実際のネットワークに接続しません。
採点はbash /opt/lab/checks/test-failures-lab/02-contract.shで直接再現できます。ファイルを保存してから、もう一度実行してください。
総試行回数と最後の例外を検査する
/root/work/test-failures-lab/test_service.pyで、常にTimeoutErrorになるsendを渡し、合計3回の呼び出しのあとでTimeoutErrorが伝播することを確認してください。
リトライ3回と合計3回の試行は別物です。この契約は、最初のリクエストを含めて3回です。
採点はbash /opt/lab/checks/test-failures-lab/03-contract.shで直接再現できます。ファイルを保存してから、もう一度実行してください。
恒久的なエラーのリトライを防ぐ
/root/work/test-failures-lab/test_service.pyで、ValueErrorを発生させるsendがちょうど1回だけ呼び出され、同じ種類の例外が伝播することをテストしてください。
例外の種類だけでなくcallsの長さも検査して、意味のない2回の追加呼び出しを検出します。
採点はbash /opt/lab/checks/test-failures-lab/04-contract.shで直接再現できます。ファイルを保存してから、もう一度実行してください。
バックオフの数列と最後の待機を確認する
/root/work/test-failures-lab/test_service.pyで、すべてのsendがTimeoutErrorのとき、sleepに渡された値がちょうど[0.1, 0.2]であることを検査してください。実際にはスリープしません。
sleep引数にdelays.appendを渡します。最後の失敗の後には次の試行がないので、待機もあってはいけません。
採点はbash /opt/lab/checks/test-failures-lab/05-contract.shで直接再現できます。ファイルを保存してから、もう一度実行してください。
戻り値とディスクの内容を一緒に確認する
/root/work/test-failures-lab/test_service.pyで、ハングルの文字列で成功させたあと、cacheファイルをUTF-8で読み込み、同じ内容が保存されていることを確認してください。
成功の結果だけを返してファイルへの書き込みを省いた実装は、戻り値のテストだけでは検出できません。
採点はbash /opt/lab/checks/test-failures-lab/06-contract.shで直接再現できます。ファイルを保存してから、もう一度実行してください。
ネットワークの失敗で既存のキャッシュが消えないようにする
/root/work/test-failures-lab/test_service.pyで、cacheに'old'をあらかじめ書き込んでから、sendを失敗させ続けてください。例外の後も、ファイルの内容が'old'のままであることを確認します。
準備 → 実行と例外の検査 → ファイルの状態の確認、という順序で構成します。ファイルが存在するかどうかだけを見てはいけません。
採点はbash /opt/lab/checks/test-failures-lab/07-contract.shで直接再現できます。ファイルを保存してから、もう一度実行してください。
保存の失敗を偽の成功に変えない
/root/work/test-failures-lab/test_service.pyで、sendが成功しても、cacheへの書き込みが失敗したらOSErrorが伝播することをテストしてください。cacheにtmp_pathのディレクトリそのものを渡すと、決定的に書き込みが失敗します。
権限の変更に依存しないエラーを作ってください。成功した文字列の戻り値だけを確認するテストとは、別の責務です。
採点はbash /opt/lab/checks/test-failures-lab/08-contract.shで直接再現できます。ファイルを保存してから、もう一度実行してください。