Alpha
Alpha

Reputation: 14046

Why Java stacktrace only returns failure in finally block?

I've written some automation tests where I use syntax like -

try { 
   // Test case steps and validations
} finally { 
   // Clean up related to test
}

NOTE: This does not have catch block as my tests do not expect exceptions.

If test fails in try as well as finally block, only failures of finally are returned on console but not of try. Simple example here(TestNG is used here for assertions) -

try {
    Assert.assertEquals(true, false, "Boolean values did not match");
} finally {
    Assert.assertEquals(100, 10, "Integer values did not match");
}

In this case, only details of finally failures are returned.

This does not help to identify actual failure of test looking at the console.

I would like to understand why Java does not return both failure details on console which can help user to identify failure cause at very first look.

Upvotes: 4

Views: 80

Answers (3)

Andreas
Andreas

Reputation: 159096

That is because a new exception thrown from a finally block replaces the exception thrown from the try block, as documented in the Java Language Specification, section 14.20.2. Execution of try-finally and try-catch-finally:

If execution of the try block completes abruptly because of a throw of a value V, then there is a choice:

[...]

If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and the throw of value V is discarded and forgotten).

Since the finally block doesn't even know of the original exception, it cannot do anything about it.

However, if you catch the original exception, you can include it as a suppressed exception (Java 7+), but it's a bit convoluted:

AssertionError originalError = null;
try {
    Assert.assertEquals(true, false, "Boolean values did not match");
} catch (AssertionError e) {
    originalError = e;
    throw e;
} finally {
    try {
        Assert.assertEquals(100, 10, "Integer values did not match");
    } catch (AssertionError e) {
        if (originalError == null)
            throw e;
        originalError.addSuppressed(e);
        throw originalError;
    }
}

Upvotes: 1

Karol Dowbecki
Karol Dowbecki

Reputation: 44952

Because under the hood Assert.assertEquals() uses Assert.fail() to throw an AssertionError to signal the error.

The exception in finally block will replace and discard the one thrown in try block as per 14.20.2. Execution of try-finally and try-catch-finally.

Upvotes: 1

Sweeper
Sweeper

Reputation: 271575

The same thing can be observed with throwing exceptions:

try {
    throw new RuntimeException("Message 1");
} finally {
    throw new RuntimeException("Message 2");
}

Here, only Message 2 is printed:

Exception in thread "main" java.lang.RuntimeException: Message 2

This is because when finally throws an exception, any exceptions in try is "discarded and forgotten":

JLS section 14.20.2

  • If the run-time type of V is not assignment compatible with a catchable exception class of any catch clause of the try statement, then the finally block is executed. Then there is a choice:

    • If the finally block completes normally, then the try statement completes abruptly because of a throw of the value V.

    • If the finally block completes abruptly for reason S, then the try statement completes abruptly for reason S (and the throw of value V is discarded and forgotten).

Upvotes: 4

Related Questions