失敗を表示する代わりに例外がスローされるという間違ったことは何ですか、またはスレッド内にアサーションを含めるべきではありませんか?
@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)
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()を呼び出して検証します。テストメソッドは必要に応じて待機し、メインスレッドのコンテキストでアサーションエラーをスローします。
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
}
元の質問のように、複数のワーカースレッドが関係している場合、それらの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
}
}
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());
}
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;
}
}