简体   繁体   中英

How to capture all uncaucht exceptions on junit tests?

If we have created a singleton object to handle an Java Exceptions, why Thread.setDefaultUncaughtExceptionHandler runs ok in Java Application Server, Java Console Application but not works on JUnit tests?

For example, the following code works:

public class Main extends Object {

    public static void main(String[] arguments) {
        Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler.getInstance());
        double a = 1/0;
    }
}

but this JUnit test not:

public class UncaughtExceptionHandlerTest {

    @Test
    public void throwException() {
        Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler.getInstance());
        double a = 1/0;
    }
}

but why? And, how can we solve this, to automatically handle all JUnit test exceptions without using a moody try catch to each test?

The JUnit will be catching all unexpected exceptions that are thrown by the unit tests on the unit test threads 1 . The normal behavior is to catch / display / record the exception as a FAILed test, and then continue with the next unit test.

This means that the there is no "uncaught exception" in the Java sense, and your uncaught exception handler is not going to be called.

It is not entirely clear what you are trying to achieve here, but I suspect that the answer would be to implement a custom runner:


1 - If the code under test spawns its own threads, the JUnit framework has no way of knowing. It certainly cannot catch / detect uncaught exceptions on those threads. However, this doesn't seem to be what you are talking about in this question.


The main motivation, is, for example, send an e-mail or perform another administrative tasks if a junit test fail. If I have a global exception handler I could do this, instead put a catch block to each test. After the handling, maybe I will throw this exception and let junit go ahead as it does.

Well if that is what you are trying to do, then you are (IMO) doing it the wrong way . There are existing runners that provide a structured report file, or a report data structure that can give you a list of all tests that passed, failed from an assertion, failed from an exception, etc. What you should do is:

  • choose an appropriate runner
  • analyse its output
  • send a single email (or whatever) if there are errors that meet your criteria.

Advantages:

  • less effort
  • you deal with all errors not just uncaught exceptions (though actually assertion failures manifest as AssertionError exceptions ...)
  • you don't spam yourself on each and every failed test.

And there's another way. Look at JUnitCore ( link ). This allows you register a listener for the various test events, and then run a bunch of tests or test suites.

The other point is that you appear to be trying to duplicate (some of) the functionality of a Continuous Integration (CI) server such as Jenkins.


You then asked why this doesn't work:

@Test
public void throwException() {
    Thread.setDefaultUncaughtExceptionHandler(/* some handler */));
    double a = 1/0;
}

An uncaught exception handler is only invoked if nothing else catches the exception. But a typical JUnit test runner catches all exceptions that propagate from each unit test using a conventional exception handler. That means that the ArithmeticException thrown in your test never reaches your handler.

Exceptions thrown by your junit @Test method are not uncaught. JUnit catches them and uses them to fail your tests.

Now, if you had started a new Thread of your own that is not running inside JUnit's try/catch execution, a thrown exception will be essentially ignored and your test will pass.

Just think of the name... Thread.setDefaultUncaughtExceptionHandler. This only covers threads that do not explicitly have an uncaught exception handler, and then it doesn't cover exceptions that are caught by the code calling your code (JUnit, etc).

Here is relevant code from ParentRunner class:

    protected final void runLeaf(Statement statement, 
                                 Description description, RunNotifier notifier) {
    EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description);
    eachNotifier.fireTestStarted();
    try {
        statement.evaluate();
    } catch (AssumptionViolatedException e) {
        eachNotifier.addFailedAssumption(e);
    } catch (Throwable e) {
        eachNotifier.addFailure(e);
    } finally {
        eachNotifier.fireTestFinished();
    }

Are you sure that jUnit isn't catching it somewhere? The method signature says that it throws Exception so I'd guess that there has to be a pretty broad catch statement up-stream.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM