JaCoCo并错过了对私有默认构造函数

时间:2016-03-11 21:00:38

标签: java code-coverage jacoco-maven-plugin private-constructor

我想看一个例子来阻止 JaCoCo 将私有空构造函数报告为Java类中的非覆盖代码。

在maven插件配置中我有

   <rule>
     <element>CLASS</element>
       <excludes>
         <exclude>JAVAC.SYNTHCLASS</exclude>
         <exclude>JAVAC.SYNTHMETH</exclude>
       </excludes>
     </element>
   </rule>

构造函数是否有类似内容?

6 个答案:

答案 0 :(得分:10)

不支持此功能。 official documentation说:

  

过滤代码,其中测试执行是可疑的或不可能的设计

     
      
  • 私有,空默认构造函数 - 假设没有调用
  •   
  • 普通的吸气者和二传手
  •   
  • 抛出AssertionErrors的块 - 如果条件(如果!断言抛出新的AssertionError),则应忽略整个块
  •   

另见:https://github.com/jacoco/jacoco/issues/298

更新:这在https://github.com/jacoco/jacoco/pull/529中得到修复,应该是0.8.0。

答案 1 :(得分:1)

无法关闭该选项。如果您迫切需要满足与覆盖相关的一些质量门,您可以随时使用变通方法并通过反射调用这些私有构造函数。

答案 2 :(得分:0)

对于这个用例,反射是完全可以接受的,很少有众所周知的类。波纹管代码可以与基于名称的自动类检测一起使用。对于带有附加断言的示例“。* Factory”类。

@Test
public void testCoverage()
    throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
    coverageSingleton(MySingleton1.class);
    coverageSingleton(MySingleton2.class);
}

private <S> void coverageSingleton(Class<S> singletonClass)
    throws SecurityException, NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
    final Constructor<S> constructor = singletonClass.getDeclaredConstructor();
    constructor.setAccessible(true);
    constructor.newInstance();
}

答案 3 :(得分:0)

根据官方文档,它将随0.8.0

一起发布
  

通过过滤代码,其中测试执行是可疑的或不可能的   的设计

     

没有参数的私有空构造函数 - Done

您可以找到详细信息here

答案 4 :(得分:-1)

这并不是解决空私有构造函数不需要覆盖的基本问题,而是实际上需要对需要调用它的空私有构造函数进行JaCoCo报告覆盖。你是怎样做的?您可以在静态初始化块中调用它。

public class MyClass {
   static {
      new MyClass();
   }
   private MyClass(){}
}

修改 结果表明,无法保证静态初始化块的执行。因此,我们仅限于使用这样的方法:

static <T> void callPrivateConstructorIfPresent(Class<T> clazz){
        try{
            Constructor<T> noArgsConstructor = clazz.getDeclaredConstructor();
            if(!noArgsConstructor.isAccessible()){
                noArgsConstructor.setAccessible(true);
                try {
                    noArgsConstructor.newInstance();
                } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) 
                {
                    e.printStackTrace();
                }
                noArgsConstructor.setAccessible(false);
            }
        } catch(NoSuchMethodException e){}
    }

答案 5 :(得分:-1)

由于0.8.0尚未发布,我创建了一个hamcrest匹配器,用于检查类是否是实用程序类,并使用反射调用私有构造函数(仅用于代码覆盖)。

https://github.com/piotrpolak/android-http-server/blob/master/http/src/test/java/ro/polak/http/utilities/IOUtilitiesTest.java

package ro.polak.http.utilities;

import org.junit.Test;


import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static ro.polak.http.ExtraMarchers.utilityClass;

public class IOUtilitiesTest {

    @Test
    public void shouldNotBeInstantiable() {
        assertThat(IOUtilities.class, is(utilityClass()));
    }
}

https://github.com/piotrpolak/android-http-server/blob/master/http/src/test/java/ro/polak/http/ExtraMarchers.java

package ro.polak.http;

import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class ExtraMarchers {

    private static final UtilClassMatcher utilClassMatcher = new UtilClassMatcher();

    public static Matcher<? super Class<?>> utilityClass() {
        return utilClassMatcher;
    }

    private static class UtilClassMatcher extends TypeSafeMatcher<Class<?>> {
        @Override
        protected boolean matchesSafely(Class<?> clazz) {
            boolean isUtilityClass = false;
            try {
                isUtilityClass = isUtilityClass(clazz);
            } catch (ClassNotFoundException | InstantiationException e) {
                // Swallowed
            }

            // This code will attempt to call empty constructor to generate code coverage
            if (isUtilityClass) {
                callPrivateConstructor(clazz);
            }

            return isUtilityClass;
        }

        @Override
        protected void describeMismatchSafely(Class<?> clazz, Description mismatchDescription) {
            if (clazz == null) {
                super.describeMismatch(clazz, mismatchDescription);
            } else {
                mismatchDescription.appendText("The class " + clazz.getCanonicalName() + " is not an utility class.");

                boolean isNonUtilityClass = true;
                try {
                    isNonUtilityClass = !isUtilityClass(clazz);
                } catch (ClassNotFoundException e) {
                    mismatchDescription.appendText(" The class is not found. " + e);
                } catch (InstantiationException e) {
                    mismatchDescription.appendText(" The class can not be instantiated. " + e);
                }

                if (isNonUtilityClass) {
                    mismatchDescription.appendText(" The class should not be instantiable.");
                }
            }
        }

        @Override
        public void describeTo(Description description) {

        }

        private void callPrivateConstructor(Class clazz) {
            try {
                Constructor<?> constructor = clazz.getDeclaredConstructor();
                constructor.setAccessible(true);
                constructor.newInstance();
            } catch (NoSuchMethodException | IllegalAccessException |
                    InstantiationException | InvocationTargetException e) {
                // Swallowed
            }
        }

        private boolean isUtilityClass(Class clazz) throws ClassNotFoundException, InstantiationException {
            boolean hasPrivateConstructor = false;
            try {
                clazz.newInstance();
            } catch (IllegalAccessException e) {
                hasPrivateConstructor = true;
            }
            return hasPrivateConstructor;
        }
    }
}