コンテンツへスキップ

Elixirの不安定なテスト

要件

不安定なテストとは、同じコードで複数回実行した際に異なる結果(合格または不合格)を生み出すテストのことです。これらはテストスイートへの信頼を損ない、誤った失敗の調査に開発者の時間を浪費させます。Tuist は不安定なテストを自動的に検出し、経時的な追跡をサポートします。

Phoenixアプリケーションの不安定なテスト

不安定な検出の仕組み#

Tuistは次の2つの方法で不安定なテストを検出します。

テストの再試行#

ExUnitは各テストを1回だけ実行するため、断続的に失敗するテストは壊れているテストと同じように見えます。再試行を有効にすると、Hexパッケージは設定した回数まで失敗したテストを再度実行します。再試行で合格したテストは不安定として報告され、すべての失敗したテストが最終的に合格すれば実行は成功します。

再試行はデフォルトで無効です。有効にするには、mix.exs内の既存のTuistオプションにtest_retriesを追加してください。

elixir
tuist: [project: "account/project", cache: true, test_retries: 2]

または、フラグまたは環境変数を使用して単一の実行に対して指定します。

bash
mix test --retries 2
TUIST_TEST_RETRIES=2 mix test

フラグは環境変数より優先され、環境変数はmix.exsより優先されます。

失敗したテストのみが再度実行され、その動作はmix test --failedと同じです。ダッシュボードには各テストの試行回数と所要時間が表示されます。--max-failuresによりExUnitが早期に停止した実行は再試行されません。失敗したテストがすべてタイムアウトした実行も再試行されません。各再試行で再びすべてのタイムアウトを待つことになるためです。また、--warnings-as-errorsを使用した場合や、umbrellaプロジェクトのルートから実行した場合も再試行はスキップされます。mix testはこれを強制しなくなるため、umbrella内のいずれかのアプリケーション内でテストを実行して再試行してください。

不安定なExUnitテストとその履歴

クロスラン検出#

テストの再試行を行わなくても、Tuistは同じコミットに対する異なるCI実行の結果を比較することで不安定なテストを検出できます。あるCI実行ではテストが合格しても、同じコミットおよびスキームラベル(--scheme)の別の実行では失敗する場合、その失敗した実行は不安定としてマークされます。

これは、再試行で捕捉できるほど一貫して失敗しないものの、断続的なCI失敗を引き起こす不安定なテストを検出するのに特に役立ちます。

不安定なテストの管理#

自動クリア#

検出とクリアはプロジェクト上の 自動化アラート を介して実行されます。すべてのプロジェクトにはデフォルトで「不安定なテスト検出」自動化が設定されており、その トリガー はテストを不安定としてマークし、リカバリー はテストが再トリガーされずに設定されたリカバリー期間(デフォルト 14 日)を経過するとフラグを解除します。設定 → 自動化 で編集して、リカバリー期間を変更したり、リカバリーアクション(例:テストの隔離も解除する)を変更したり、リカバリーを完全に無効にしてテストを手動でクリアするまで不安定なままにしておくことができます。

手動管理#

テストケースの詳細ページから、テストを不安定として手動でマークまたはマーク解除することもできます。これは以下のような場合に役立ちます。

  • 修正作業中に既知の不安定なテストを認識したい場合
  • インフラストラクチャの問題によりテストが誤ってフラグ付けされた場合

不安定なテストの隔離#

隔離により、修正中に不安定なテストがCIをブロックすることを防ぎます。ダッシュボードのテストページで状態をミュートまたはスキップに変更するか、設定 → オートメーションの下でオートメーションを設定し、テストが不安定になったときにミュートまたはスキップし、回復したら再度有効にします。テストを実行する前に、mix testはプロジェクトのすべての隔離されたテストを読み込んで適用します。

  • ミュート: テストは実行され、失敗は報告されますが、失敗がミュートされたテストのみの場合、実行は合格となります。再試行は失敗したミュートテストを再度実行するため、不安定なテストは合格して不安定として報告される可能性があります。
  • スキップ: テストは実行されません。mix testは実行開始前にスキップされたテストを一覧表示します。
plaintext
Tuist: skipping 1 quarantined test:
MyApp.CheckoutTest: create_order/2 times out
Tuist: 1 muted test run, and their failures do not fail the run.

ミュートされていないテストが失敗した場合、または--max-failuresによりExUnitが早期に停止した場合、実行は通常どおり失敗します。

制限事項
  • 隔離を適用するのはHexパッケージの最近のバージョンのみです。以前のバージョンはミュートおよびスキップされたテストを他のテストと同様に実行するため、ミュートされたテストの失敗は実行を失敗させます。依存する前にtuist_exを更新してください。
  • ミュートされたテストは、--warnings-as-errorsを使用する場合や、umbrellaプロジェクトのルートから実行する場合は適用されません。その場合、それらの失敗は実行を失敗させます。代わりにumbrellaの各アプリケーション内でテストを実行してください。
  • スキップはモジュールではなく、describeブロックと名前でテストを一致させます。別のモジュールで同じdescribeブロックと名前を持つテストもスキップされます。スキップがテストを選択する方法を参照してください。
  • --only、--include、またはpath:lineで選択した場合、あるいはtestで宣言されていないテスト(StreamDataのpropertyなど)の場合、スキップされたテストでも実行されます。
  • mix testが隔離されたテストを読み取れない場合、すべてのテストを実行し、すべての失敗をカウントします。
  • 公開プロジェクトであっても、隔離されたテストを読み取るには認証情報が必要です。TUIST_TOKEN、mix tuist.loginセッション、または継続的インテグレーションのIDトークンがない実行では、隔離は適用されません。これには、通常シークレットなしで実行されるフォークからのプルリクエストも含まれます。
  • --coverを使用する場合、スキップされたテストは削除扱いとなり、そのテストのみがカバーしていた行は未カバーとして報告されます。

mix testは、サマリーの後に、誤ってスキップから除外されたテストと、それでも実行されたスキップされたテストの名前を表示します。

スキップがテストを選択する方法#

スキップされたテストは、フルネームに対するExUnitフィルター(例: --exclude "test:test create_order/2 times out")によって除外されます。ExUnitフィルターはモジュールも一致させることができないため、次のようになります。

  • 別のモジュールで同じdescribeブロックと名前を持つテストも除外されます。mix testは実行後にそれらのテストの名前を表示します。
  • --only、--include、またはpath:lineで選択したテストは、ExUnitが除外フィルターの後に包含フィルターを適用するため、スキップされていても実行されます。testで宣言されていないテスト(StreamDataのpropertyなど)も同様です。mix testは実行後にそれらのテストの名前を表示します。

隔離されたテストを再度実行する#

修正を確認するなど、すべてのテストを実行してすべての失敗をカウントするには、--no-quarantineを渡します。

bash
mix test --no-quarantine

後の実行に含めるには、テストのページで状態を有効に戻してください。スキップされたテストは、再度実行されるまで新しい結果を生成しません。

mix testが隔離されたテストを読み取れない場合、警告を表示してそれらを除くすべてのテストを実行します。認証情報または構成済みプロジェクトがない場合、警告なしですべてのテストを実行します。

Hexパッケージはまだ新しいテストのストレステストを行いません。

Slack通知#

Slack連携で不安定なテストのアラートを設定し、テストが不安定になった際に通知を受け取りましょう。

不安定な状態のクエリ#

データはHTTP経由で利用できます。ルート、フィルター、レスポンスフィールドの完全な一覧については、APIリファレンスのテストケースエンドポイントを参照してください。テストケースを不安定としてマークまたは解除するには、is_flakyを指定してPATCH /api/projects/{account_handle}/{project_handle}/tests/test-cases/{test_case_id}を送信するか、update_test_caseモデルコンテキストプロトコルツールを使用してください。