Код покрытия, наконец, блок
У меня есть следующая конструкция кода:
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