Code coverage finally block
Asked Answered
S

3

12

I have the following code construction:

try {
   //some code
}
catch(CustomException custExc) {
  //log
}
catch(CustomException2 custExc2) {
  //log
}
catch(Exception exc) {
  //log
}
finally {
  //some code
} 

I wrote unit tests: the first one covered the situation when an exception is not thrown (executing only try block code and finally block code) and 3 other are which of them is covered each catch block at once (executing try block, one of catch block and finally block). Problem is that the Eclipse Emma plugin showed that I didn't covered finally block. Any ideas why can it happen?

Semiconscious answered 28/8, 2015 at 21:35 Comment(0)
A
10

In the Java bytecode (at least since Java 1.6) there's no special construct for the finally block, so it is actually duplicated many times. For example, consider the following method:

public static void main(String[] args) {
    try {
        System.out.println("In try");
        if(args.length > 0)
            return;
        System.out.println("No args");
    }
    catch(RuntimeException ex) {
        System.out.println("In catch");
    }
    finally {
        System.out.println("In finally");
    }
}

This code is effectively compiled to something like this:

public static void main(String[] args) {
    try {
        System.out.println("In try");
        if(args.length > 0) {
            System.out.println("In finally");
            return;
        }
        System.out.println("No args");
    }
    catch(RuntimeException ex) {
        System.out.println("In catch");
        System.out.println("In finally");
    }
    catch(<any exception> t) {
        System.out.println("In finally");
        throw t;
    }
    System.out.println("In finally");
}

This is not a fully equivalent code, because if new exception occurs during the System.out.println("In finally"); (before the return), then it will not be caught. However it shows the rough idea that the finally block is duplicated here four times. It can be duplicated much more times if you have several ways to exit your try block and especially if you have nested try-finally blocks. Also note the <any exception> special catch added. It will appear in the bytecode even if you explicitly write catch(Throwable t).

As code coverage tools like Emma or JaCoCo work on byte-code level, they are unaware that these four "In finally" printlns are actually the same statement in the source code. It's possible to perform a bytecode analysis and quite robustly determine which parts of bytecode correspond to the single source finally block (I actually wrote such analyzer once), but it's not very easy problem and has some non-trivial caveats. You also should take into account that different compilers (for example, javac and ecj) produce somewhat different layout of finally blocks. So seems that this work was not done in popular coverage tools and they just consider different finally block copies as different code.

In your particular case seems that @bobbel is right: you did not test the uncaught exception case (that <any exception> catch).

Audient answered 31/8, 2015 at 11:11 Comment(1)
"Also note the <any exception> special catch added. It will appear in the bytecode even if you explicitly write catch(Throwable t)." - I believe that this is wrong, i.e. catch(java/lang/Throwable) and catch(any) - two distinct exception handlers in bytecode, even if Throwable is a base class of all errors and exceptions.Prothonotary
S
4

While I was testing some cases, I found out, that you probably don't have covered the case, when a not catched Exception is thrown.

Given the following example:

import java.text.ParseException;
import java.text.SimpleDateFormat;

import org.junit.Test;

public class CodeCoverageFinallyTest {
    @Test
    public void testMyMethod() {
        myMethod("2015-08-31");
        myMethod("wrongFormat");
    }

    private void myMethod(final String source) {
        try {
            new SimpleDateFormat("yyyy-MM-dd").parse(source);
        } catch (final ParseException e) {
            System.out.println("catch ParseException");
        } finally {
            System.out.println("finally");
        }
    }
}

This example will only catch one of the two branches in the finally block because you don't test the case, if a unchecked exception (i.e. a NullPointerException) will be thrown.

So, if you change your testcase a bit, you will catch all branches in the finally block:

public void testMyMethod() {
    myMethod("2015-08-31");
    myMethod("wrongFormat");
    myMethod(null);   // also cover the case, that an unchecked and unhandled exception
                      // will be thrown
}

In my other testcase I had a sligthly different case with some if-else-if construct.

import org.junit.Test;

public class CodeCoverageIfElseTest {
    @Test
    public void testMyMethod() {
        myMethod("2015-08-31");
        myMethod("wrongFormat");
    }

    private void myMethod(final String source) {
        if ("2015-08-31".equals(source)) {
            System.out.println("Correct format");
        } else if ("wrongFormat".equals(source)) {
            System.out.println("Incorrect format");
        }
    }
}

Here the else if didn't catch the second branch because, what if the if and else if condition won't be true? It will also be caught, if you provide other values than the both in the if-else-if statement.

Supersensual answered 31/8, 2015 at 10:45 Comment(0)
S
0

Screenshot about green coverage

Yes, the missing branch, when an uncaught throwable thrown.

If you're curious about this topic, I advice you my github page, where I try all these things out: https://github.com/bachoreczm/basicjava

Selfopinionated answered 4/3, 2016 at 18:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.