web-dev-qa-db-ja.com

スレッドでのjunitアサートが例外をスローする

失敗を表示する代わりに例外がスローされるという間違ったことは何ですか、またはスレッド内にアサーションを含めるべきではありませんか?

 @Test
 public void testComplex() throws InterruptedException {
  int loops = 10;
  for (int i = 0; i < loops; i++) {
   final int j = i;
   new Thread() {
    @Override
    public void run() {
     ApiProxy.setEnvironmentForCurrentThread(env);//ignore this
     new CounterFactory().getCounter("test").increment();//ignore this too
     int count2 = new CounterFactory().getCounter("test").getCount();//ignore
     assertEquals(j, count2);//here be exceptions thrown. this is line 75
    }
   }.start();
  }
  Thread.sleep(5 * 1000);
  assertEquals(loops, new CounterFactory().getCounter("test").getCount());
}

スタックトレース

Exception in thread "Thread-26" junit.framework.AssertionFailedError: expected:<5> but was:<6>
    at junit.framework.Assert.fail(Assert.Java:47)
    at junit.framework.Assert.failNotEquals(Assert.Java:277)
    at junit.framework.Assert.assertEquals(Assert.Java:64)
    at junit.framework.Assert.assertEquals(Assert.Java:195)
    at junit.framework.Assert.assertEquals(Assert.Java:201)
    at com.bitdual.server.dao.ShardedCounterTest$3.run(ShardedCounterTest.Java:77)
23
antony.trupe

JUnitフレームワークは、テストを実行しているメインスレッドのアサーションエラーのみをキャプチャします。新しいスポーンスレッド内からの例外を認識しません。それを正しく行うには、スレッドの終了状態をメインスレッドに伝える必要があります。スレッドを正しく同期し、ある種の共有変数を使用して、ネストされたスレッドの結果を示す必要があります。

編集:

これが役立つ一般的な解決策です:

class AsynchTester{
    private Thread thread;
    private AssertionError exc; 

    public AsynchTester(final Runnable runnable){
        thread = new Thread(new Runnable(){
            public void run(){
                try{            
                    runnable.run();
                }catch(AssertionError e){
                    exc = e;
                }
            }
        });
    }

    public void start(){
        thread.start();
    }

    public void test() throws InterruptedException{
        thread.join();
        if (exc != null)
            throw exc;
    }
}

コンストラクターでランナブルを渡す必要があります。次に、start()を呼び出してアクティブ化し、test()を呼び出して検証します。テストメソッドは必要に応じて待機し、メインスレッドのコンテキストでアサーションエラーをスローします。

36
Eyal Schneider

Eyal Schneiderの答え への小さな改善:
ExecutorService を使用すると、Callableを送信でき、スローされた例外またはエラーは、によって再スローされます。 Futureを返しました。
したがって、テストは次のように記述できます。

@Test
public void test() throws Exception {
  ExecutorService es = Executors.newSingleThreadExecutor();
  Future<?> future = es.submit(() -> {
    testSomethingThatMightThrowAssertionErrors();
    return null;
  });

  future.get(); // This will rethrow Exceptions and Errors as ExecutionException
}
9
MyKey_

元の質問のように、複数のワーカースレッドが関係している場合、それらの1つを単に結合するだけでは不十分です。理想的には、Eyalの回答のように、アサーションの失敗をメインスレッドに報告しながら、すべてのワーカースレッドが完了するのを待つ必要があります。

ConcurrentUnit を使用してこれを行う方法の簡単な例を次に示します。

public class MyTest extends ConcurrentTestCase {
    @Test
    public void testComplex() throws Throwable {
        int loops = 10;
        for (int i = 0; i < loops; i++) {
            new Thread(new Runnable() {
                public void run() {
                    threadAssertEquals(1, 1);
                    resume();
                }
            }).start();
        }

        threadWait(100, loops); // Wait for 10 resume calls
    }
}
6
Jonathan

JUnitはThrowableを拡張するAssertionErrorをスローし、Exceptionの同じ親を持ちます。スレッドの失敗アサートをキャッチし、静的フィールドに保存して、最後に他のスレッドが何らかのアサートに失敗したかどうかをメインスレッドでチェックインできます。

まず、静的フィールドを作成します

private volatile static Throwable excepcionTE = null;

次に、アサートをtry/catchでラップし、AssertionErrorをキャッチします

        try
    {
      assertTrue("", mensaje.contains("1234"));
    }
    catch (AssertionError e)
    {
      excepcionTE = e;
      throw e;
    }

そして最後に、そのフィールドのメインスレッドをチェックインします

 if (excepcionTE != null)
{
  excepcionTE.printStackTrace();
  fail("Se ha producido una excepcion en el servidor TE: "
      + excepcionTE.getMessage());
}
0
Riki Gomez

RunnablesとThreadsの両方で機能するこのパターンを使用することになりました。これは主に@EyalSchneiderの回答から着想を得ています。

private final class ThreadUnderTestWrapper extends ThreadUnderTest {
    private Exception ex;

    @Override
    public void run() {
        try {
            super.run();
        } catch ( Exception ex ) {
            this.ex = ex;
        }
    }

    public Exception getException() throws InterruptedException {
        super.join(); // use runner.join here if you use a runnable. 
        return ex;
    }
}
0
Snicolas