线程中的junit断言抛出异常

时间:2010-04-07 23:02:49

标签: java 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());
}

栈跟踪

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)

5 个答案:

答案 0 :(得分:33)

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

答案 1 :(得分:5)

Eyal Schneider's answer的小改进:
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
}

答案 2 :(得分:4)

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

JUnit抛出Throwable扩展的AssertionError,它具有Exception的相同父级。您可以捕获线程的失败断言,然后保存在静态字段中,最后检查主线程是否另一个线程在某些断言中失败。

首先,创建静态字段

Test* t1 = new Test(0);
Test& t2 = *t1;
int i = t2.getData();

第二,将断言包装在try / catch中,并捕获AssertionError

t->getData()

最后,在主线程中检查该字段

private volatile static Throwable excepcionTE = null;