2010-04-07 25 views
17

Co robię źle, że wyjątek jest rzucany zamiast pokazywania awarii, czy też nie powinienem mieć zapewnień wewnątrz wątków?junit twierdzenie w wątku zgłasza wyjątek

@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) 
+0

Whats your stacktrace? –

+0

@Frederik dodano stacktrace –

+0

Dlaczego tworzysz nowy wątek w tym teście? Mam na myśli, dlaczego h @ $! czy chcesz utworzyć wątki w teście jednostkowym? –

Odpowiedz

30

Ramy JUnit rejestruje tylko błędy twierdzenie w głównym wątkiem test. Nie jest świadomy wyjątków z nowych wątków odradzania. Aby zrobić to dobrze, należy podać stan zakończenia wątku do głównego wątku. Powinieneś poprawnie zsynchronizować wątki i użyć jakiejś współdzielonej zmiennej, aby wskazać wynik zagnieżdżonego wątku.

EDIT:

Oto ogólny rozwiązanie, które mogą pomóc:

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; 
    } 
} 

Należy zdać IT runnable w konstruktorze, a następnie po prostu wywołać start(), aby włączyć, i test () do sprawdzenia poprawności. Metoda testowa będzie czekać w razie potrzeby i spowoduje zgłoszenie błędu potwierdzenia w kontekście wątku głównego.

+1

* "Powinieneś zsynchronizować wątki poprawnie ..." * w tym przykładzie, prosta droga dla głównego wątku wywoływania 'join()' w wątku potomnym ... i pozbycia się 'snu (5000) 'zadzwoń. –

+0

Wezwanie do snu trochę pachniało, ale nie zastanawiałem się nad tym, ponieważ był to kod testu jednostkowego, ale na pewno będę używał poprawnej metody, o której teraz wiem. –

4

W przypadku wielu wątków roboczych, takich jak w pierwotnym pytaniu, po prostu dołączenie do jednego z nich nie jest wystarczające. Najlepiej będzie, gdy zaczekasz na zakończenie wszystkich wątków roboczych, nadal zgłaszając błędy asercji z powrotem do głównego wątku, na przykład w odpowiedzi Eyala.

Oto prosty przykład jak to zrobić przy użyciu 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 
    } 
} 
0

skończyło się stosując ten wzór działał zarówno Runnables i nici. To jest w dużej mierze inspirowany z odpowiedzią @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; 
    } 
} 
2

Niewielka poprawa do Eyal Schneider's answer:
ExecutorService pozwala złożyć Callable i wszelkie rzucane wyjątki lub błędy są rethrown przez zwróconego Future.
konsekwencji test może być zapisany jako:

@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 
} 
Powiązane problemy