Code coverage finally block

Alexey Semenyuk picture Alexey Semenyuk · Aug 28, 2015 · Viewed 9.6k times · Source

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?

Answer

Tagir Valeev picture Tagir Valeev · Aug 31, 2015

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 catched. 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 uncatched exception case (that <any exception> catch).