1. ホーム
  2. java

[解決済み] 失敗したJUnitのテストをすぐに再実行するには?

2023-05-09 01:18:37

質問

JUnitルールやそれに類するもので、失敗したテストにもう一度実行する機会を与える方法はありますか。

背景 私は、JUnit で書かれた大規模な Selenium2-WebDriver テストのセットを持っています。非常に積極的なタイミング(クリック後の短い待ち時間だけ)のために、いくつかのテスト(100のうちの1つ、そして常に異なるもの)は、サーバーが時々少し遅く応答するために失敗することがあります。しかし、待ち時間をあまり長くすることはできませんし、そうするとテストがずっとかかってしまうからです)。-- ですから、この使用例では、たとえ再試行が必要であってもテストが緑色であることは許容されると思います。

もちろん、2 out of 3 majority(失敗したテストを3回繰り返し、そのうち2回が正しければ正しいとして扱う)があった方が良いのですが、これは将来の改善でしょう。

どのように解決するのですか?

を使って行うことができます。 テストルール . これによって、必要な柔軟性を得ることができます。TestRule はテストの周りにロジックを挿入することができるので、再試行ループを実装することになります。

public class RetryTest {
    public class Retry implements TestRule {
        private int retryCount;

        public Retry(int retryCount) {
            this.retryCount = retryCount;
        }

        public Statement apply(Statement base, Description description) {
            return statement(base, description);
        }

        private Statement statement(final Statement base, final Description description) {
            return new Statement() {
                @Override
                public void evaluate() throws Throwable {
                    Throwable caughtThrowable = null;

                    // implement retry logic here
                    for (int i = 0; i < retryCount; i++) {
                        try {
                            base.evaluate();
                            return;
                        } catch (Throwable t) {
                            caughtThrowable = t;
                            System.err.println(description.getDisplayName() + ": run " + (i+1) + " failed");
                        }
                    }
                    System.err.println(description.getDisplayName() + ": giving up after " + retryCount + " failures");
                    throw caughtThrowable;
                }
            };
        }
    }

    @Rule
    public Retry retry = new Retry(3);

    @Test
    public void test1() {
    }

    @Test
    public void test2() {
        Object o = null;
        o.equals("foo");
    }
}

の中心は TestRulebase.evaluate() で、これがテストメソッドを呼び出します。この呼び出しを中心に、再試行のループを配置します。テストメソッドで例外が発生した場合 (アサーションの失敗は、実際には AssertionError である) 場合は、テストは失敗しているので再試行します。

もうひとつ役に立つことがあります。その場合は、上記の Retry クラスにメソッドの特定のアノテーションに対するテストを追加すればよいでしょう。 Description にはメソッドのアノテーションのリストが含まれます。これについての詳細な情報は、私の答えを参照してください。 どのように@RunWithもAOPも使用せずに、各JUnit @Testメソッドの前にいくつかのコードを個別に実行するのですか? .

カスタムのTestRunnerを使用する

これはCKuckの提案ですが、独自のRunnerを定義することができます。そのためには BlockJUnit4ClassRunner を拡張し、runChild()をオーバーライドします。詳細については、私の回答 スイートのJUnitメソッドルールを定義するにはどうすればよいですか? . この回答は、スイート内のすべてのメソッドに対してコードを実行する方法を定義する方法について詳しく説明しています。