結論
Unity Test RunnerのPlay Modeテストでは、テストコードが直接Saveを書いていなくても、Play Mode終了時の通常保存処理が実Saveへ到達する場合があります。
今回の原因は、direct -runTestsが独自のSave隔離処理を通らず、テスト終了後のOnApplicationQuitから実Saveへ書き込んでいたことでした。
対策は、すべてのテスト実行経路でSaveをテスト専用の保存先へ切り替え、Play Mode終了後まで隔離を維持することです。
症状
今回確認した症状は次のとおりです。
- Play Modeテストはすべて成功していた
- ゲーム状態そのものは変わっていなかった
- テスト終了後、実JSON Saveのtimestampだけが更新されていた
- Saveファイルのbyte hashがテスト前後で変化していた
- Consoleには原因を示す例外が出ていなかった
原因
direct -runTestsが独自のSave隔離処理を通っていなかった
独自wrapperからテストを実行した場合は、実Saveではなく一時的なSaveへ切り替えていました。
一方、Unityをコマンドラインから直接-runTestsで起動すると、そのwrapperを通りませんでした。
そのため、テスト終了後に通常のSaveサービスへ戻り、次の保存処理が実Saveへ到達していました。
OnApplicationQuit -> SaveNowAsync -> SaveAsync
TearDown後にも保存処理が続いていた
Play Modeテストでは、テスト本体やTearDownが終わった後も、Play Mode終了やEditor終了に伴う処理が続きます。
TearDown直後にSave隔離を解除すると、その後のOnApplicationQuitが実Saveへ書き込む可能性があります。
確認方法
まず、テスト前後で実Saveファイルを比較します。
- テスト前にSaveファイルをコピーする
- byte hashを記録する
- Play Modeテストを実行する
- テスト終了後に再度hashを計算する
- hashが違う場合はJSON差分を確認する
ゲーム状態に差がなくても、timestampや最終保存時刻だけが変わっていないか確認してください。
次に、Play Mode終了時の保存経路を調べます。
OnApplicationQuit
Save
SaveAsync
SaveNow
独自のテストwrapperやSave overrideを使用している場合は、direct -runTestsでも同じ隔離処理が有効か確認します。
解決方法
Saveをテスト専用の保存先へ切り替える
Play Modeテスト中は実Saveを使用せず、次のいずれかへ切り替えます。
- メモリ上のSave実装
- テスト専用ディレクトリ
- テスト専用Saveサービス
ゲームコード側で保存先やSaveサービスを差し替えられるようにすると、実Saveへ触れずにテストできます。
direct -runTestsも隔離対象にする
独自wrapperだけを開始条件にせず、Unity Test Runnerの実行開始を検知してSave隔離を開始します。
今回の対策では、direct -runTestsを含む実行中の内部Save書き込みを止め、テスト専用Saveへ切り替えました。
Play Mode終了後まで隔離を維持する
TearDownやRunFinishedで直ちに実Saveへ戻さず、Play Mode終了とTest Runnerの処理停止を確認してから隔離を解除します。
Editorが途中終了する可能性がある場合は、開始前のSaveを保持し、次回起動時に復元できるようにします。
関連記事
PlayerPrefsが消える問題については、Unity Test RunnerでPlayerPrefsが消える原因|DeleteAllに注意で説明しています。