概要
Swift 5.5 で入った Concurrency を使ったテストで、await 時に想定外のことが起きて結果が返ってこずテストが永遠に終わらないということが起こったので、テストにタイムアウトを設定する方法を調べてまとめました。
動作検証はすべて Xcode 13.2.1 で行いました。
TestPlan を設定する
まず、タイムアウトを設定するために TestPlan を有効にします。タイムアウトの設定のみであればビルド時に特定のコマンドラインオプションを渡すことでも可能なようですが、細かく設定しようと思うと TestPlan を作ってしまった方が便利なのでそちらの方法をとることにします。
Xcode の window 上部の Scheme をクリックして Edit Scheme... から Test の Info タブを開きます。右下の方に Convert to use Test Plans... というボタンがあるのでこれをクリックします。
3つのオプションが出てくるので、一番上の Create Test Plan from scheme を選びます。
作成された TestPlan の Configuration タブを開き TestExecution > Test Timeouts を On にすることでタイムアウトが有効になります。
タイムアウト値の設定
executionTimeAllowance という XCTestCase のプロパティを編集することでタイムアウト値を設定することができます。
ドキュメントにあるように、 executionTimeAllowance の値自体は秒数ですが実際のタイムアウト値は分単位での切り上げになることに注意しましょう。例えば 1 を設定すると1秒の切り上げの1分、 150 を設定すると2分30秒の切り上げの3分でテストがタイムアウトするようになります。この振る舞いは分かりづらいので executionTimeAllowance の値は 60 120 180 のようにちょうど分単位になるように設定するのがよい気がします。
例えば executionTimeAllowance を 60 にしてテスト中に 61 秒待つようにすると、期待通りにテストが落ちてくれます。エラーメッセージもなかなかわかりやすいと思います。
以上でテストにタイムアウトを設定することができましたが、すべてのテストでいちいち executionTimeAllowance を設定するのは大変です。TestPlan の Test Timeouts の下の Default Test Execution Time Allowance という項目で executionTimeAllowance のデフォルト値の設定ができます。以下のように設定すれば、すべてのテストが1分でタイムアウトするようになります。
ちなみに Default Test Execution Time Allowance 自体のデフォルト値は 600 なので、 Test Timeouts を On にした時点ですべてのテストが10分でタイムアウトするようになっていたことになります。
Default Test Execution Time Allowance で決まるタイムアウト値を各テストメソッドごとに上書きしたい場合は executionTimeAllowance に値を入れることでそちらが優先されます。
まとめ
- テストがなんらかの原因で hang しないようにテストメソッドにタイムアウトを設定するとよい
- TestPlan を有効にして
Test TimeoutsをOnにすることでテストがタイムアウトするようになる - タイムアウトまでの時間は、 TestPlan の設定の
Default Test Execution Time AllowanceとXCTestCaseのexecutionTimeAllowanceプロパティでそれぞれ設定できる- 後者の設定が優先される
- どちらの設定も単位は秒だが、タイムアウトまでの時間は分単位での切り上げになることに注意




