web-dev-qa-db-ja.com

JUnitテストケースを作成してスレッドとイベントをテストするには

Javaコードが1つの(メイン)スレッドで機能します。メインスレッドから、サーバーコールを行う新しいスレッドを生成します。サーバーコールが完了した後、新しいスレッドでいくつかの作業を行い、その後、コードはメインスレッドに参加します。

サーバー呼び出しを行うためにEclipse Jobsを使用しています。

知りたいのですが、このためのJUnitテストケースをどのように記述すればよいですか。

15
stalwalk

簡単にテストできるように、コードを再構成する必要がある場合があります。

テスト用にいくつかの異なる領域を確認できます。

  1. スレッド管理コード:スレッドを起動し、おそらく結果を待つコード
  2. スレッドで実行される「ワーカー」コード
  3. 複数のスレッドがアクティブな場合に発生する可能性のある並行性の問題

スレッド管理コードがワーカーの詳細にとらわれないように実装を構造化します。次に、モックワーカーを使用してスレッド管理のテストを有効にすることができます。たとえば、特定の方法で失敗するモックワーカーを使用すると、管理コードの特定のパスをテストできます。

Workerコードを実装して、分離して実行できるようにします。サーバーのモックを使用して、これを個別に単体テストできます。

並行性テストについては、Abhijeet Kashniaが提供するリンクが役立ちます。

10
djna

これが ConcurrentUnit が作成された目的です。一般的な使用法は次のとおりです。

  1. いくつかのスレッドを生成します
  2. メインスレッドを待機またはスリープさせる
  3. ワーカースレッド内からアサーションを実行します(ConcurrentUnitを介して、メインスレッドに報告されます)。
  4. すべてのアサーションが完了したら、ワーカースレッドの1つからメインスレッドを再開します

詳細については、ConcurrentUnitページを参照してください。

6
Jonathan

あなたはあなたのモックコードを実行したかもしれず、サーバー呼び出しが機能することを確認するための簡単な統合テストが必要かもしれないと私は推測しています。

スレッドのテストの難しさの1つは、スレッド自体の性質にあります-それらは同時に発生します。つまり、コードの結果をテストする前に、スレッドがジョブを完了するまで待機するJUnitテストコードを作成する必要があります。これはコードをテストするための良い方法ではなく、信頼性が低い場合がありますが、通常、コードが機能しているかどうかについてある程度の知識があることを意味します。

例として、コードは次のようになります。

@Test
public void myIntegrationTest() throws Exception {

   // Setup your test


   // call your threading code
   Results result = myServerClient.doThreadedCode();

   // Wait for your code to complete
   sleep(5);

   // Test the results
   assertEquals("some value",result.getSomeValue());

}


private void sleep(int seconds) {

    try {
        TimeUnit.SECONDS.sleep(seconds);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

私は本当にこれが好きではなく、モックを好み、他の答えに同意します。しかし、スレッドをテストする必要がある場合、これは私がうまくいくと思うアプローチの1つです。

2
roghughe

サーバー呼び出しが実際に行われたことを確認するために、モックフレームワークを使用することをお勧めします。スレッドのユニットテストについて: マルチスレッドアプリケーションのユニットテスト

2

Abhijeet Kashnia が提供するリソースは役立つかもしれませんが、あなたが何を達成しようとしているかはわかりません。

モックを使用してユニットテストを実行し、コードを検証できます。これにより、同時実行性はテストされませんが、カバレッジが提供されます。統合テストを作成して、スレッドが期待どおりに作成および結合されていることを確認できますが、これは同時実行の問題を保証するものではありません。ほとんどの同時発生の問題は、予測できないタイミングバグが原因で発生するため、正確にテストすることができません。

2
Jean

唯一の問題が結果を待っている場合は、スレッドを生成するために ExecutorService を使用します。 Runnable および Callable の両方として作業ジョブを受け入れることができます。後者を使用すると、結果として Future オブジェクトが返され、結果を待つことができます。とにかくExecutorServiceの使用を検討する必要があります。私が理解しているところによると、多くのスレッドが作成されるため、これはエグゼキューターサービスの完全なユースケースです。

class AnyClass {
    private ExecutorService threadPool = Executors.newFixedThreadPool(5);

    public List<Future<Integer>> anyMethod() {
        List<Future> futures = new ArrayList<>();

        futures.add(threadPool.submit(() -> {
            // Do your job here
            return anyStatusCode;
        }));

        futures.add(threadPool.submit(() -> {
            // Do your other job here
            return anyStatusCode;
        }));

        return futures;
    }
}

そしてテストクラス:

class TestAnyClass {
    @Test
    public void testAnyMethod() {
        AnyClass anyObject = new AnyClass();

        List<Future<Integer>> futures = anyObject.anyMethod();
        CompletableFuture[] completable = futures.toArray(new CompletableFuture[futures.size()]);

        // Wait for all
        CompletableFuture.allOf(completable).join();
    }
}
0
itachi

Thread.startを使用した非同期メソッドをテストするための私の解決策は次のとおりです。

public class MyClass {      
   public void doSomthingAsynchrone() {
      new Thread(() -> {
         doSomthing();
      }).start();
   }

   private void doSomthing() {
   }
}

@RunWith(PowerMockRunner.class)
@PrepareForTest(MyClass.class)
public class MyClassTest {
   ArgumentCaptor<Runnable> runnables = ArgumentCaptor.forClass(Runnable.class);

   @InjectMocks
   private MyClass myClass;

   @Test
   public void shouldDoSomthingAsynchrone() throws Exception {  

      // create a mock for Thread.class
      Thread mock = Mockito.mock(Thread.class);

      // mock the 'new Thread', return the mock and capture the given runnable
      whenNew(Thread.class).withParameterTypes(Runnable.class)
            .withArguments(runnables.capture()).thenReturn(mock);

      myClass.doSomthingAsynchrone();

      runnables.getValue().run();

      /**
       *  instead of 'runnables.getValue().run();' you can use a real thread.start
       *
       *   MockRepository.remove(Thread.class);
       *   Thread thread = new Thread(runnables.getValue());
       *   thread.start();
       *   thread.join();
       **/

      verify(myClass, times(1)).doSomthing();
   }
}
0