cobertura 未能通过反射测试 class

cobertura fails tests for class with reflection

我 运行 mvn clean cobertura:cobertura install 在我的项目上,但其中一个测试失败了。

测试尝试使用 反射 编写的构建器访问 class。

public MyClass(Builder builder) throws Exception {
    Field[] classFields = MyClass.class.getDeclaredFields();
    Field[] classBuilderFields = Builder.class.getDeclaredFields();
    for (int i = 0; i < classBuilderFields.length; i++) {
        Field fieldInClass = classFields[i];
        Field fieldInBuilder = classBuilderFields[i];
        fieldInBuilder.setAccessible(true);
        String fieldNameInClass = fieldInClass.getName();
        String fieldNameInBuilder = fieldInBuilder.getName();
        if (null != fieldNameInClass && null != fieldNameInBuilder && fieldNameInClass.equals(fieldNameInBuilder)) {
            fieldInClass.set(this, fieldInBuilder.get(builder));
        } else {
            throw new Exception("");
        }
    }
}

我调试了代码,发现 cobertura "implents" 在我的 class __cobertura_counters 中有一个自己的变量。这是例外情况:

java.lang.IllegalAccessException: Can not set static final [I field com.domain.MyClass.__cobertura_counters to
    at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:73)
    at sun.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:77)
    at sun.reflect.UnsafeQualifiedStaticObjectFieldAccessorImpl.set(UnsafeQualifiedStaticObjectFieldAccessorImpl.java:77)
    at java.lang.reflect.Field.set(Field.java:741)
    at com.domain.MyClass.<init>(MyClass.java:217)
    at com.domain.MyClassTest.setUp(MyClassTest.java:57)
    at com.domain.MyClass$Builder.build(MyClass.java:197)

我该如何解决这个问题?

问题是,您尝试修改的字段是最终字段。可以删除 final 修饰符,并且可以做到。 polygenelubricants 发布了一段很好的代码,您可以使用它来删除修饰符。您可以在这里找到它:Using reflection to change static final File.separatorChar for unit testing?

是的,Cobertura 添加此字段是为了跟踪访问了哪些代码。出于类似的目的,其他代码覆盖工具添加了类似的字段。生成的字段具有此签名:

public static final transient int[] __cobertura_counters;

您可以做的只是忽略代码中的瞬态字段。你可以这样做:

if (!Modifier.isTransient(fieldInClass.getModifiers())) {
    // do a thing
}

但是请注意,non-generated 代码也可以有瞬态字段,因此根据您要反映的代码,这可能是也可能不是一个选项。否则,您唯一的选择(AFAIK)就是简单地检查是否 fieldInClass.getName().equals("__cobertura_counters").

通常情况下,编译器生成的字段会被标记为"synthetic"。例如,javac 为 non-static 内部 类 生成的 this[=13=] 字段是合成的。很容易检查:fieldInClass.isSynthetic()。但是,出于某种原因,Cobertura 不遵循此约定。