チャンク処理・進捗再開の単体テストは入力分解 mock を固定値にしてはいけない
バックエンド
テスト
設計判断
判断
運用
バッチやワーカーが「入力を一定サイズの chunk に分割し、永続化した進捗カーソル(処理済み件数)から再開する」設計を単体テストするとき、入力を分解・パースして work-list を返す依存(パーサやプランナ)を、入力やカーソルに関係なく固定の小さな結果を返す mock にすると、肝心のロジックがまるごと未検証のまま緑になる。
何が壊れても気づけないか
- chunk 分割(要素数からチャンク数を決める処理)。固定1件しか流れないと常に1チャンクになり、分割境界が検証されない。
- 再開オフセット計算(処理済み件数 をチャンクサイズで割って開始チャンク index を出し、処理済みチャンクをスキップする処理)。カーソルが常に0だと開始 index も常に0で、再開分岐が一度も走らない。実装を「常に先頭から」に書き換えても通る=再処理(二重更新)を防ぐ最重要の振る舞いが空通しになる。
- 複数チャンクをまたぐ成功・失敗・処理済み件数の加算と、チャンクごとの進捗永続化。1チャンクでは「即終了」と「ループ継続」が区別できず、success と failed の別集計も縛れない。
対処(判断基準)
- 分解 mock の戻り値を入力サイズを反映した件数にする(チャンクサイズを跨ぐ件数=最低2チャンク)。
- 再開は、エンティティの進捗カーソルを途中値(例: 1チャンク分処理済み)にしたうえで、開始チャンク以降だけが処理され、処理済み分が再実行されないことをアサートする。
- 途中の一時失敗は、先頭チャンク成功後に次チャンクを失敗させ、成功分までの進捗が永続化されてから再スローされることを、永続化呼び出しの引数で確認する。
- 恒久失敗は、複数チャンクで一部だけ失敗させ、後続チャンクが継続処理され success/failed/processed が別々に正しく集計されることを確認する。
気づき方
書いたテストに対し「実装を意図的に壊したらこのアサーションは落ちるか」を1ケースずつ想像する。mock の戻り値をそのままアサートしているだけ(mock echo)や、分岐の入口に必要な前提(入力サイズ・進捗カーソル)が固定されている場合、その分岐は未検証。独立した敵対的レビューを別に走らせるとこの種の空通しを検出しやすい。