Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Code coverage finally block

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?

like image 203
Alexey Semenyuk Avatar asked Aug 28 '15 21:08

Alexey Semenyuk


People also ask

Can we write code in finally block?

But finally is useful for more than just exception handling — it allows the programmer to avoid having cleanup code accidentally bypassed by a return , continue , or break . Putting cleanup code in a finally block is always a good practice, even when no exceptions are anticipated.

When the finally block code is?

The finally block in java is used to put important codes such as clean up code e.g. closing the file or closing the connection. The finally block executes whether exception rise or not and whether exception handled or not. A finally contains all the crucial statements regardless of the exception occurs or not.

How can you stop your code from calling finally block?

The finally block follows a try block or a catch block. A finally block of code always executes, irrespective of occurrence of an Exception. You cannot skip the execution of the final block. Still if you want to do it forcefully when an exception occurred, the only way is to call the System.

What is block coverage?

A block policy is an all-risk insurance policy providing coverage against risks faced by goods transported or stored by third parties. Commonly found in commercial insurance, a block policy is designed to protect businesses from property damage.


2 Answers

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.

like image 84
bobbel Avatar answered Oct 01 '22 00:10

bobbel


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

like image 43
Tagir Valeev Avatar answered Oct 01 '22 00:10

Tagir Valeev