线程中的junit断言抛出exception

如果抛出exception而不是显示失败,或者我在线程内部没有断言,我做错了什么?

@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: but was: 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框架仅捕获运行测试的主线程中的断言错误。 它不知道新的生成线程中的exception。 为了做到正确,您应该将线程的终止状态传递给主线程。 您应该正确地同步线程,并使用某种共享变量来指示嵌套线程的结果。

编辑:

这是一个通用的解决方案,可以帮助:

 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()来validation。 如有必要,测试方法将等待,并将在主线程的上下文中抛出断言错误。

对Eyal Schneider的回答略有改进:
ExecutorService允许提交Callable并且返回的Future将重新抛出任何抛出的exception或错误。
因此,测试可以写成:

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

在涉及多个工作线程的情况下,例如在原始问题中,仅仅加入其中一个是不够的。 理想情况下,您需要等待所有工作线程完成,同时仍然将断言失败报告回主线程,例如在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 } } 

我最终使用这种模式,它与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; } }