代码覆盖最终阻止

时间:2015-08-28 21:35:02

标签: java unit-testing try-catch emma

我有以下代码构造:

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插件显示我没有覆盖finally块。任何想法为什么会发生?

3 个答案:

答案 0 :(得分:6)

在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>捕获)。

答案 1 :(得分:1)

在我测试某些案例时,我发现,当一个未被捕获的异常被抛出时,你可能没有覆盖这个案例。

给出以下示例:

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语句中提供除两者之外的其他值,也会被捕获。

答案 2 :(得分:0)

Screenshot about green coverage

是的,丢失的分支,当一个未被捕获的扔掉的时候。

如果你对这个话题感到好奇,我会告诉你我的github页面,在那里我尝试了所有这些:https://github.com/bachoreczm/basicjava