Покрытие кода окончательно блокируется

У меня есть следующая конструкция кода:

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

Я написал модульные тесты: первый рассмотрел ситуацию, когда исключение не выбрасывается (выполняется только код блока try и, наконец, блок-код), а три других - это тот, который из них охватывает каждый блок catch сразу (выполнение блока try, один из блоков catch и, наконец, блок). Проблема в том, что плагин Eclipse Emma показал, что я не закрыл последний блок. Любые идеи, почему это может случиться?

Ответ 1

В байт-коде Java (по крайней мере, с Java 1.6) нет специальной конструкции для блока finally, поэтому она фактически дублируется много раз. Например, рассмотрим следующий метод:

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");
    }
}

Этот код эффективно скомпилирован примерно так:

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");
}

Это не полностью эквивалентный код, потому что если во время System.out.println("In finally"); (перед возвратом) возникает новое исключение, то оно не будет выхвачено. Однако он показывает грубую идею о том, что блок finally дублируется здесь четыре раза. Его можно дублировать гораздо чаще, если у вас есть несколько способов выйти из блока try, и особенно если у вас есть вложенные блоки try-finally. Также обратите внимание на добавленный специальный урок <any exception>. Он появится в байт-коде, даже если вы явно пишете catch(Throwable t).

Поскольку инструменты покрытия кода, такие как Emma или JaCoCo, работают на уровне байтового кода, они не знают, что эти четыре "In finally" printlns на самом деле являются тем же самым выражением в исходном коде. Можно выполнить анализ байт-кода и достаточно точно определить, какие части байт-кода соответствуют единому блоку окончательного блока (я на самом деле написал такой анализатор один раз), но это не очень простая проблема и имеет некоторые нетривиальные оговорки. Вы также должны учитывать, что разные компиляторы (например, javac и ecj) создают несколько разный макет блоков finally. Похоже, что эта работа не была выполнена в популярных инструментах охвата, и они просто рассматривают разные окончательные копии блоков как разные коды.

В вашем конкретном случае кажется, что @bobbel прав: вы не тестировали случай исключенного исключения (этот <any exception> catch).

Ответ 2

Пока я тестировал некоторые случаи, я узнал, что вы, вероятно, не рассматривали этот случай, когда выбрано исключение исключений.

В следующем примере:

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");
        }
    }
}

Этот пример поймает только один из двух ветвей в блоке finally, потому что вы не проверите это событие, если будет выбрано исключение (исключение NullPointerException).

Итак, если вы немного измените свой тестовый файл, вы поймаете все ветки в блоке finally:

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

В моем другом тестовом сценарии у меня был довольно разный случай с некоторой конструкцией if-else-if.

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");
        }
    }
}

Здесь else if не поймал вторую ветвь, потому что, если условие if и else if не будет истинным? Он также будет пойман, если вы укажете другие значения, чем те, которые содержатся в инструкции if-else-if.

Ответ 3

Снимок экрана о зеленом покрытии

Да, отсутствующая ветка, когда недвижущая броска бросается.

Если вам интересно об этой теме, я советую вам мою страницу github, где я пробую все это: https://github.com/bachoreczm/basicjava