java - org - junit assert en thread throws exception
org.junit.assert maven (4)
El marco JUnit captura solo errores de aserciones en el hilo principal que ejecuta la prueba. No tiene conocimiento de excepciones desde dentro de nuevos hilos de generación. Para hacerlo bien, debe comunicar el estado de finalización del hilo al hilo principal. Debe sincronizar los subprocesos correctamente y usar algún tipo de variable compartida para indicar el resultado del subproceso anidado.
EDITAR:
Aquí hay una solución genérica que puede ayudar:
class AsynchTester{
private Thread thread;
private volatile 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;
}
}
Debería pasarle el ejecutable en el constructor, y luego simplemente llamar a start () para activar, y test () para validar. El método de prueba esperará si es necesario y arrojará el error de aserción en el contexto del subproceso principal.
¿Qué estoy haciendo mal al lanzar una excepción en lugar de mostrar una falla, o no debería haber aserciones dentro de los hilos?
@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());
}
StackTrace
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)
Cuando se trate de múltiples hilos de trabajo, como en la pregunta original, simplemente unirse a uno de ellos no es suficiente. Idealmente, querrá esperar a que se completen todos los subprocesos de trabajo mientras sigue informando los errores de aserción en el hilo principal, como en la respuesta de Eyal.
Aquí hay un ejemplo simple de cómo hacer esto usando 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
}
}
Terminé usando este patrón, funciona con Runnables y Threads. Está inspirado principalmente en la respuesta de @Eyal Schneider:
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;
}
}
Una pequeña mejora a la respuesta de Eyal Schneider :
El ExecutorService
permite enviar un Callable
y cualquier Excepción o Errores lanzados por el Future
devuelto.
En consecuencia, la prueba se puede escribir como:
@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
}