Junit assert в исключении нитей
Что я делаю неправильно, что исключение вызывается вместо того, чтобы показывать сбой, или я не должен иметь утверждений внутри потоков?
@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)
Ответы
Ответ 1
Инфраструктура 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;
}
}
Вы должны передать ему runnable в конструкторе, а затем просто вызвать start() для активации и test() для проверки. Тестовый метод будет ждать, если это необходимо, и выдаст ошибку подтверждения в контексте основного потока.
Ответ 2
Небольшое улучшение ответа Эяля Шнайдера:
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
}
Ответ 3
Если речь идет о нескольких рабочих потоках, например, в исходном вопросе, простое присоединение к одному из них недостаточно. В идеале вам нужно будет дождаться завершения всех рабочих потоков, все еще сообщая об ошибках утверждения в основной поток, например, в ответе 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
}
}
Ответ 4
В результате я использовал этот шаблон, который работает как с Runnables, так и с Threads. Это во многом вдохновлено ответом @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;
}
}
Ответ 5
JUnit генерирует AssertionError, которая распространяется на Throwable, у него тот же родитель 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());
}