Код покрытия, наконец, блок

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

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

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

3 ответа

В байт-коде 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 - фактически то же самое утверждение в исходном коде. Можно выполнить анализ байт-кода и достаточно точно определить, какие части байт-кода соответствуют единственному блоку finally (на самом деле я написал такой анализатор один раз), но это не очень простая проблема и имеет некоторые нетривиальные предостережения. Вы также должны принять во внимание, что разные компиляторы (например, javac и ecj) создают несколько разную компоновку блоков finally. Похоже, что эта работа не была проделана в популярных инструментах покрытия, и они просто рассматривают разные, наконец, блочные копии как разные коды.

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

Когда я тестировал некоторые случаи, я обнаружил, что вы, вероятно, не покрыли случай, когда выдается исключение без перехвата.

Учитывая следующий пример:

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 заявление.

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

Да, недостающая ветвь, когда непроизвольно брошенный бросок.

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

Другие вопросы по тегам