yeg*_*256 106 java testing code-coverage
这是代码:
package com.XXX;
public final class Foo {
private Foo() {
// intentionally empty
}
public static int bar() {
return 1;
}
}
Run Code Online (Sandbox Code Playgroud)
这是测试:
package com.XXX;
public FooTest {
@Test
void testValidatesThatBarWorks() {
int result = Foo.bar();
assertEquals(1, result);
}
@Test(expected = java.lang.IllegalAccessException.class)
void testValidatesThatClassFooIsNotInstantiable() {
Class cls = Class.forName("com.XXX.Foo");
cls.newInstance(); // exception here
}
}
Run Code Online (Sandbox Code Playgroud)
工作正常,课程经过测试.但Cobertura表示,该类私有构造函数的代码覆盖率为零.我们如何为这样的私有构造函数添加测试覆盖率?
Jav*_*mae 134
我并不完全赞同Jon Skeet.我认为,如果您能轻松获得覆盖率并消除报道中的噪音,那么您应该这样做.告诉你的覆盖工具忽略构造函数,或者把理想主义放在一边,写下面的测试并完成它:
@Test
public void testConstructorIsPrivate() throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
Constructor<Foo> constructor = Foo.class.getDeclaredConstructor();
assertTrue(Modifier.isPrivate(constructor.getModifiers()));
constructor.setAccessible(true);
constructor.newInstance();
}
Run Code Online (Sandbox Code Playgroud)
Jon*_*eet 81
好吧,有些方法可以使用反射等 - 但是它真的值得吗?这是一个永远不应该被调用的构造函数,对吗?
如果有一个注释或类似的东西你可以添加到类中以使Cobertura明白它不会被调用,那就这样做:我认为值得通过箍来人为地增加覆盖率.
编辑:如果没有办法,只需稍微减少覆盖率.请记住,覆盖范围应该是对您有用的东西- 您应该负责该工具,而不是相反.
Arc*_*ano 75
虽然它不一定用于覆盖,但是我创建了这个方法来验证实用程序类是否定义良好并且还进行了一些覆盖.
/**
* Verifies that a utility class is well defined.
*
* @param clazz
* utility class to verify.
*/
public static void assertUtilityClassWellDefined(final Class<?> clazz)
throws NoSuchMethodException, InvocationTargetException,
InstantiationException, IllegalAccessException {
Assert.assertTrue("class must be final",
Modifier.isFinal(clazz.getModifiers()));
Assert.assertEquals("There must be only one constructor", 1,
clazz.getDeclaredConstructors().length);
final Constructor<?> constructor = clazz.getDeclaredConstructor();
if (constructor.isAccessible() ||
!Modifier.isPrivate(constructor.getModifiers())) {
Assert.fail("constructor is not private");
}
constructor.setAccessible(true);
constructor.newInstance();
constructor.setAccessible(false);
for (final Method method : clazz.getMethods()) {
if (!Modifier.isStatic(method.getModifiers())
&& method.getDeclaringClass().equals(clazz)) {
Assert.fail("there exists a non-static method:" + method);
}
}
}
Run Code Online (Sandbox Code Playgroud)
我已将完整的代码和示例放在https://github.com/trajano/maven-jee6/tree/master/maven-jee6-test中
Ben*_*rdy 19
我私有了我的静态实用函数类的构造函数,以满足CheckStyle.但就像原版海报一样,我让Cobertura抱怨测试.起初我尝试了这种方法,但这不会影响覆盖率报告,因为构造函数从未实际执行过.所以所有这些测试都是如果构造函数保持私有 - 这在后续测试中通过可访问性检查变得多余.
@Test(expected=IllegalAccessException.class)
public void testConstructorPrivate() throws Exception {
MyUtilityClass.class.newInstance();
fail("Utility class constructor should be private");
}
Run Code Online (Sandbox Code Playgroud)
我选择了Javid Jamae的建议并使用了反射,但添加了断言来捕捉任何搞乱被测试类的人(并将其命名为测试以指示高级邪恶).
@Test
public void evilConstructorInaccessibilityTest() throws Exception {
Constructor[] ctors = MyUtilityClass.class.getDeclaredConstructors();
assertEquals("Utility class should only have one constructor",
1, ctors.length);
Constructor ctor = ctors[0];
assertFalse("Utility class constructor should be inaccessible",
ctor.isAccessible());
ctor.setAccessible(true); // obviously we'd never do this in production
assertEquals("You'd expect the construct to return the expected type",
MyUtilityClass.class, ctor.newInstance().getClass());
}
Run Code Online (Sandbox Code Playgroud)
这太过分了,但我必须承认我喜欢100%方法覆盖的温暖模糊感觉.
使用Java 8,可以找到其他解决方案.
我假设您只是想创建几个公共静态方法的实用程序类.如果您可以使用Java 8,那么您可以使用它interface
.
package com.XXX;
public interface Foo {
public static int bar() {
return 1;
}
}
Run Code Online (Sandbox Code Playgroud)
Cobertura没有建设者也没有抱怨.现在您只需要测试您真正关心的线条.
以下内容对我来说是使用 Lombok 注释 @UtilityClass 创建的类,它会自动添加私有构造函数。
@Test
public void testConstructorIsPrivate() throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
Constructor<YOUR_CLASS_NAME> constructor = YOUR_CLASS_NAME.class.getDeclaredConstructor();
assertTrue(Modifier.isPrivate(constructor.getModifiers())); //this tests that the constructor is private
constructor.setAccessible(true);
assertThrows(InvocationTargetException.class, () -> {
constructor.newInstance();
}); //this add the full coverage on private constructor
}
Run Code Online (Sandbox Code Playgroud)
虽然在手动编写私有构造函数时, constructor.setAccessible(true) 应该可以工作,但使用 Lombok 注释不起作用,因为它强制它。Constructor.newInstance() 实际上测试构造函数是否被调用,这完成了构造函数本身的覆盖。使用assertThrows,您可以防止测试失败并管理异常,因为这正是您所期望的错误。尽管这是一种解决方法,而且我不理解“线路覆盖率”与“功能/行为覆盖率”的概念,但我们可以在这个测试中找到意义。事实上,您确信实用程序类实际上有一个私有构造函数,该构造函数在通过反射调用时也可以正确抛出异常。希望这可以帮助。
测试无法执行任何操作的代码背后的原因是实现100%的代码覆盖率并注意代码覆盖率下降的时间.否则我总是会想,嘿,我没有100%的代码覆盖率,但它可能是因为我的私有构造函数.这使得很容易发现未经测试的方法,而无需检查它只是一个私有构造函数.随着您的代码库的增长,您实际上会感受到100%而不是99%的温暖感觉.
IMO最好在这里使用反射,否则你将不得不得到一个更好的代码覆盖工具忽略这些构造函数或以某种方式告诉代码覆盖工具忽略该方法(可能是注释或配置文件),因为那样你就会卡住使用特定的代码覆盖工具.
在一个完美的世界中,所有代码覆盖工具都会忽略属于最终类的私有构造函数,因为构造函数在那里作为"安全"度量没有别的:)
我将使用此代码:
@Test
public void callPrivateConstructorsForCodeCoverage() throws SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException
{
Class<?>[] classesToConstruct = {Foo.class};
for(Class<?> clazz : classesToConstruct)
{
Constructor<?> constructor = clazz.getDeclaredConstructor();
constructor.setAccessible(true);
assertNotNull(constructor.newInstance());
}
}
Run Code Online (Sandbox Code Playgroud) 然后只需向数组添加类即可.
较新版本的Cobertura具有内置支持,可忽略琐碎的getter / setter / constructor:
https://github.com/cobertura/cobertura/wiki/Ant-Task-Reference#ignore-trivial
忽略琐碎的事
忽略琐碎的事情就可以排除包含一行代码的构造函数/方法。一些示例仅包括对超级构造函数的调用,getter / setter方法等。要包括ignore平凡的参数,请添加以下内容:
<cobertura-instrument ignoreTrivial="true" />
Run Code Online (Sandbox Code Playgroud)
或在Gradle构建中:
cobertura {
coverageIgnoreTrivial = true
}
Run Code Online (Sandbox Code Playgroud)