web-dev-qa-db-ja.com

同時JUnitテスト

大規模なJUnitテストスイートがあり、次の2つの理由ですべてのテストを同時に実行したいです。

  • 複数のコアを活用してテストスイート全体をより高速に実行する
  • うまくいけば、スレッドセーフでないグローバルオブジェクトに起因するいくつかのエラーを検出します

私はこれがいくつかのコードをリファクタリングしてスレッドセーフにすることを強制することを認識していますが、それは良いことだと思います:-)

JUnitですべてのテストを同時に実行する最良の方法は何ですか?

68
mikera

JUnitに固定されていますか? TestNGは、すぐに使用できる優れたマルチスレッドテストを提供し、JUnitテストと互換性があります(いくつかの変更が必要です)。たとえば、次のようなテストを実行できます。

_@Test(threadPoolSize = 3, invocationCount = 9,  timeOut = 10000)
public void doSomething() {
...
}
_

これは、doSomething()メソッドが3つの異なるスレッドによって9回呼び出されることを意味します。

TestNGを強くお勧めします。

31
Chandradeep

私はまさにこの質問に対する答えを探していましたが、ここの答えと他の場所で読んだものに基づいて、既存のテストを使用して既存のテストを並行して実行する簡単なすぐに使える方法は現在ないようですJUnit。または、私はそれを見つけられませんでした。そこで、私はそれを実現する簡単なJUnitランナーを作成しました。気軽に使用してください。 MultiThreadedRunnerクラスの完全な説明とソースコードについては、 http://falutin.net/2012/12/30/multithreaded-testing-with-junit/ を参照してください。このクラスを使用すると、次のように既存のテストクラスに注釈を付けることができます。

@RunWith(MultiThreadedRunner.class)
21
Mike Sokolov

次のコードは、ドイツ語の本 JUnit Profiwissen から取得した要件を満たしている必要があります。 。

JUnit 4.6は、テストの並列実行を提供するParallelComputerクラスを導入しました。ただし、この機能は、親ランナーのカスタムスケジューラを設定する可能性を提供するJUnit 4.7まで公開されていませんでした。

_public class ParallelScheduler implements RunnerScheduler {

    private ExecutorService threadPool = Executors.newFixedThreadPool(
        Runtime.getRuntime().availableProcessors());

    @Override
    public void schedule(Runnable childStatement) {
        threadPool.submit(childStatement);
    }

    @Override
    public void finished() {
        try {
            threadPool.shutdown();
            threadPool.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            throw new RuntimeException("Got interrupted", e);
        }
    }
}

public class ParallelRunner extends BlockJUnit4ClassRunner {

    public ParallelRunner(Class<?> klass) throws InitializationError {
        super(klass);
        setScheduler(new ParallelScheduler());
    }
}
_

テストクラスに@RunWith(ParallelRunner.class)アノテーションを付けると、各メソッドは独自のスレッド内で実行されます。さらに、実行中のマシンで利用可能なCPUコアと同数のアクティブスレッド(のみ)が存在します。

複数のクラスを並行して実行する必要がある場合は、次のようにカスタマイズされたスイートを定義できます。

_public class ParallelSuite extends Suite {

    public ParallelSuite(Class<?> klass, RunnerBuilder builder) 
      throws InitializationError {
        super(klass, builder);
        setScheduler(new ParallelScheduler());
    }
}
_

@RunWith(Suite.class)@RunWith(ParallelSuite.class)を変更します

F.e.の機能を活用することもできます。前の例のようにWildcardPatternSuiteの代わりにそのスイートから直接拡張することにより、Suiteを作成します。これにより、さらに単体テストをフィルタリングできます。任意の_@Category_-注釈付きカテゴリUnitTestのみを並行して実行するTestSuiteは、次のようになります。

_public interface UnitTest {

}

@RunWith(ParallelSuite.class)
@SuiteClasses("**/*Test.class")
@IncludeCategories(UnitTest.class)
public class UnitTestSuite {

}
_

簡単なテストケースは次のようになります。

_@Category(UnitTest.class)
@RunWith(MockitoJUnitRunner.class)
public class SomeClassTest {

    @Test
    public void testSomething() {
        ...
    }
}
_

UnitTestSuiteは、使用可能なCPUコアの数に応じて、Testで終わり、@Category(UnitTest.class)が並行して指定されているサブディレクトリにある各クラスを実行します。

それよりも簡単になるかどうかはわかりません:)

21
Roman Vottner

どうやらMathieu Carbouが並行処理の実装を行ったようです。

http://Java.dzone.com/articles/concurrent-junit-tests

OneJunit

@RunWith(ConcurrentJunitRunner.class)
@Concurrent(threads = 6)
public final class ATest {

    @Test public void test0() throws Throwable { printAndWait(); }
    @Test public void test1() throws Throwable { printAndWait(); }
    @Test public void test2() throws Throwable { printAndWait(); }
    @Test public void test3() throws Throwable { printAndWait(); }
    @Test public void test4() throws Throwable { printAndWait(); }
    @Test public void test5() throws Throwable { printAndWait(); }
    @Test public void test6() throws Throwable { printAndWait(); }
    @Test public void test7() throws Throwable { printAndWait(); }
    @Test public void test8() throws Throwable { printAndWait(); }
    @Test public void test9() throws Throwable { printAndWait(); }

    void printAndWait() throws Throwable {
        int w = new Random().nextInt(1000);
        System.out.println(String.format("[%s] %s %s %s",Thread.currentThread().getName(), getClass().getName(), new Throwable       ().getStackTrace()[1].getMethodName(), w));
        Thread.sleep(w);
    }
}

複数のJUnit:

@RunWith(ConcurrentSuite.class)
@Suite.SuiteClasses({ATest.class, ATest2.class, ATest3.class})
public class MySuite {
}
6
molavec

HavaRunner を試すこともできます。デフォルトで並行してテストを実行するJUnitランナーです。

HavaRunnerには便利なスイートもあります。クラスにアノテーション@PartOf(YourIntegrationTestSuite.class)を追加することで、テストをスイートのメンバーとして宣言できます。このアプローチは、スイートクラスでスイートメンバーシップを宣言するJUnitのアプローチとは異なります。

さらに、HavaRunnerスイートは、埋め込みWebアプリケーションコンテナなどの重いオブジェクトを初期化する場合があります。次に、HavaRunnerはこの重いオブジェクトを各スイートメンバーのコンストラクターに渡します。これにより、グローバルな可変状態が促進され、並列化が困難になるため、問題のある@BeforeClassおよび@AfterClassアノテーションの必要性がなくなります。

最後に、HavaRunnerにはシナリオがあります–異なるデータに対して同じテストを実行する方法です。シナリオにより、テストコードを複製する必要が減ります。

HavaRunnerは、2つの中規模のプロジェクトJavaプロジェクトでテスト済みです。

追伸私はHavaRunnerの著者です。ご意見をお寄せください。

1
Lauri Lehmijoki