java - 尝试资源的 8 个分支 - 可以进行 jacoco 覆盖吗?

标签 java code-coverage bytecode jacoco try-with-resources

我有一些使用资源的 try 代码,在 jacoco 中它只覆盖了一半。所有的源代码行都是绿色的,但我得到一个黄色的小符号,告诉我只有 8 个分支中的 4 个被覆盖。

enter image description here

我无法弄清楚所有分支是什么,以及如何编写覆盖它们的代码。三个可能的地方抛出 PipelineException。它们是 createStageList()processItem() 和隐含的 close()

  1. 不抛出任何异常,
  2. createStageList()
  3. 抛出异常
  4. processItem()
  5. 抛出异常
  6. close()
  7. 抛出异常
  8. processItem()close()
  9. 抛出异常

我想不出任何其他案例,但我仍然只涵盖了 8 个案例中的 4 个。

有人可以向我解释为什么它是 4 of 8 并且无论如何都要击中所有 8 个分支吗?我不擅长解密/阅读/解释字节码,但也许你是...... :) 我已经看过 https://github.com/jacoco/jacoco/issues/82 ,但它和它引用的问题都没有太大帮助(除了注意这是由于编译器生成的 block )

嗯,就在我写完这篇文章时,我想到了我上面提到的可能不会测试哪些情况......如果我做对了,我会发布一个答案。我确信这个问题及其答案无论如何都会对某人有所帮助。

编辑:不,我没找到。抛出 RuntimeExceptions(不由 catch block 处理)不再覆盖任何分支

最佳答案

好吧,我无法告诉您 Jacoco 的确切问题是什么,但我可以向您展示 Try With Resources 是如何编译的。基本上,有很多编译器生成的开关来处理不同点抛出的异常。

如果我们采用以下代码并编译它

public static void main(String[] args){
    String a = "before";

    try (CharArrayWriter br = new CharArrayWriter()) {
        br.writeTo(null);
    } catch (IOException e){
        System.out.println(e.getMessage());
    }

    String a2 = "after";
}

然后反汇编,我们得到

.method static public main : ([Ljava/lang/String;)V
    .limit stack 2
    .limit locals 7
    .catch java/lang/Throwable from L26 to L30 using L33
    .catch java/lang/Throwable from L13 to L18 using L51
    .catch [0] from L13 to L18 using L59
    .catch java/lang/Throwable from L69 to L73 using L76
    .catch [0] from L51 to L61 using L59
    .catch java/io/IOException from L3 to L94 using L97
    ldc 'before'
    astore_1
L3:
    new java/io/CharArrayWriter
    dup
    invokespecial java/io/CharArrayWriter <init> ()V
    astore_2
    aconst_null
    astore_3
L13:
    aload_2
    aconst_null
    invokevirtual java/io/CharArrayWriter writeTo (Ljava/io/Writer;)V
L18:
    aload_2
    ifnull L94
    aload_3
    ifnull L44
L26:
    aload_2
    invokevirtual java/io/CharArrayWriter close ()V
L30:
    goto L94
L33:
.stack full
    locals Object [Ljava/lang/String; Object java/lang/String Object java/io/CharArrayWriter Object java/lang/Throwable
    stack Object java/lang/Throwable
.end stack
    astore 4
    aload_3
    aload 4
    invokevirtual java/lang/Throwable addSuppressed (Ljava/lang/Throwable;)V
    goto L94
L44:
.stack same
    aload_2
    invokevirtual java/io/CharArrayWriter close ()V
    goto L94
L51:
.stack same_locals_1_stack_item
    stack Object java/lang/Throwable
.end stack
    astore 4
    aload 4
    astore_3
    aload 4
    athrow
L59:
.stack same_locals_1_stack_item
    stack Object java/lang/Throwable
.end stack
    astore 5
L61:
    aload_2
    ifnull L91
    aload_3
    ifnull L87
L69:
    aload_2
    invokevirtual java/io/CharArrayWriter close ()V
L73:
    goto L91
L76:
.stack full
    locals Object [Ljava/lang/String; Object java/lang/String Object java/io/CharArrayWriter Object java/lang/Throwable Top Object java/lang/Throwable
    stack Object java/lang/Throwable
.end stack
    astore 6
    aload_3
    aload 6
    invokevirtual java/lang/Throwable addSuppressed (Ljava/lang/Throwable;)V
    goto L91
L87:
.stack same
    aload_2
    invokevirtual java/io/CharArrayWriter close ()V
L91:
.stack same
    aload 5
    athrow
L94:
.stack full
    locals Object [Ljava/lang/String; Object java/lang/String
    stack 
.end stack
    goto L108
L97:
.stack same_locals_1_stack_item
    stack Object java/io/IOException
.end stack
    astore_2
    getstatic java/lang/System out Ljava/io/PrintStream;
    aload_2
    invokevirtual java/io/IOException getMessage ()Ljava/lang/String;
    invokevirtual java/io/PrintStream println (Ljava/lang/String;)V
L108:
.stack same
    ldc 'after'
    astore_2
    return
.end method

对于不会说字节码的人来说,这大致相当于下面的伪Java。我不得不使用 goto,因为字节码并不真正对应 Java 控制流。

如您所见,有很多情况可以处理被抑制异常的各种可能性。能够涵盖所有这些情况是不合理的。事实上,第一个 try block 上的 goto L59 分支是不可能到达的,因为第一个 catch Throwable 将捕获所有异常。

try{
    CharArrayWriter br = new CharArrayWriter();
    Throwable x = null;

    try{
        br.writeTo(null);
    } catch (Throwable t) {goto L51;}
    catch (Throwable t) {goto L59;}

    if (br != null) {
        if (x != null) {
            try{
                br.close();
            } catch (Throwable t) {
                x.addSuppressed(t);
            }
        } else {br.close();}
    }
    break;

    try{
        L51:
        x = t;
        throw t;

        L59:
        Throwable t2 = t;
    } catch (Throwable t) {goto L59;}

    if (br != null) {
        if (x != null) {
            try{
                br.close();
            } catch (Throwable t){
                x.addSuppressed(t);
            }
        } else {br.close();}
    }
    throw t2;
} catch (IOException e) {
    System.out.println(e)
}

关于java - 尝试资源的 8 个分支 - 可以进行 jacoco 覆盖吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17354150/

相关文章:

java - 为什么DataNode无法下载文件?

java - Hibernate:使用相同连接列的多个关系

unit-testing - java中的修改条件/决策覆盖率分析工具

unit-testing - Grails 单元测试模拟服务与分配服务实例

c - 如何在字节码虚拟机中使用 libuv

java - 模拟一个返回 future 以抛出异常的方法

java - 我的二维数组行不旋转

ruby - 如何获取我的 Sinatra 应用程序的代码覆盖率统计信息?

java - 为什么内部类构造函数中的外部类对象的 LocalVariableTable 中没有条目(Java 字节码)

compilation - 为什么在执行时而不是在安装时编译字节码 JIT?