开发者

How do I test this method's expected behavior in Java: it spawn a thread, and throws an exception under certain conditions

Suppose that I have a method which spawns a new thread and do some work. Under certain conditions, the newly spawn thread would throw a certain type of exception, which terminates the entire process. I would like to write JUnit tests to verify this behavior. Is there a way to do it?

The method is:

private void foo() {
  new Thread() {
    @Override void run() {开发者_如何学Go
      throw new CertainException("exception messages");
    }
  }.start();
}

In test (conceptually):

public testExceptionThrownFromNewThread() throws Exception {
  try {
    foo();
    Thread.sleep(5000);  // wait for the exception to be thrown
    fail();
  } catch (CertainException e) {
    assertEquals(e.message, "exception messages");
  }
}

This test doesn't work because the exception spawn from the other thread cannot be caught.


If you want to test just the code inside of the run() method, refactor it ouf of the foo() method (probably into a Runnable) and test it separately without running it from a thread.

private void foo() {
    new Thread(new MyRunnable()).start();
}

public class MyRunnable implements Runnable {
    public void run() {
    ....
    }
}

Now you can instantiate a MyRunnable object and call the run() method from your test without needing to start a thread.

EDIT

Testing of the thread creation could be done by using a ThreadFactory Mock. (as Jon Skeet pointed out).


You could overwrite the default UncaughtExceptionHandler for Threads. It gets called whenever a Thread throws an exception. In this handler, you can check whether the expected exception is equal to the thrown exception and e.g. test for messages or count the occurences of the exception. By using a CountDownLatch, you can also check whether the exceptions are thrown in time and how many of them you expect.

This works even if you do not have access to the Thread created by the class under test. If you have access to it though, there is certainly an easier approach, e.g. refactoring the class under test and introduce an Exception Listener or alike. Make the class under test better testable also improves the design, e.g. by removing the dependency on Threads and directly test the body of the run() method which you could externalize.

public class ThreadExceptionTest {

private void foo() {
    new Thread(new Runnable() {
        @Override
        public void run() {
            throw new RuntimeException("exception messages");
        }
    }).start();
}

@Test
public void testFoo() throws Exception {
    final CountDownLatch latch = new CountDownLatch(1);
    final RuntimeException expectedException = new RuntimeException("exception messages");
    UncaughtExceptionHandler eh = new UncaughtExceptionHandler() {

        @Override
        public void uncaughtException(Thread t, Throwable e) {
            if (e.getMessage().equals(expectedException.getMessage()))
                latch.countDown();
        }
    };
    Thread.setDefaultUncaughtExceptionHandler(eh);
    foo();
    assertTrue(latch.await(100,TimeUnit.MILLISECONDS));
}

}


Well, unit tests are supposed to verify results of method calls, not implementation details.

In your library, if thread terminates, how does it affect library user? Maybe computations won't be finished and end results won't be recored in database? Then check database. Maybe thread will stop doing some periodic tasks (like cleanup)? Then check whether cleanup is still being done.

And if exception thrown won't affect user in any way, then there's nothing to check. Because whether exception is thrown or not is just an implementation details (user will never see it).


One option is to make the capability to start a thread a dependency - which you can specify using the existing ThreadFactory interface. Then in your unit test you can provide a specialist ThreadFactory which wraps the given Runnable in order to record exceptions etc.

You'll be able to test that:

  • The ThreadFactory was used
  • The thread was started
  • The operation threw an exception
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜