views:

49

answers:

4

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() {
      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.

+4  A: 

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).

crazymaik
+1  A: 

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).

Nikita Rybak
I agree with that some things are supposed to be done with unit tests while some things are not. Let's say I'm doing TDD, then there needs to be way to define such behavior via tests, not necessarily unit tests in the tradition way. The specification I want to define through tests is: "when something goes wrong, the system should fail with a specific exception with a specific message."
Frank
A: 

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
Jon Skeet
+2  A: 

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

}

mhaller
Thanks for your solution. However, I'm still not sure if changing the global status of uncaught exception handler for every thread is the right thing to do, simply based on the belief I hold that tests shouldn't have side effects.
Frank
Get the old one, save it and reset it afterwards in a finally{}. Anyway, you're right about the side-effects. Are you able to change the class-under-test to introduce a ThreadFactory or better yet, introduce a Job/Tasks abstraction?
mhaller