JUnit:仅使用静态方法测试助手类

时间:2012-03-14 10:31:36

标签: java junit cobertura

我正在使用JUnit4和Cobertura的静态方法测试一个帮助器类。测试方法很容易,已经完成。

然而,cobertura表明该类完全没有被测试覆盖,因为它没有在任何地方实例化。

我不想创建这个类的实例(它是一个帮助类),所以第一个解决方案是隐藏构造函数(这通常是帮助类的好方法)。

然后cobertura抱怨空私人构造函数不受测试覆盖。

对于这种情况,是否有任何解决方案可以实现100%的代码覆盖率?

顶级管理层(在这种情况下)需要代码覆盖率,因此对于我来说,获得100%的特定课程非常有帮助。

6 个答案:

答案 0 :(得分:31)

有几种解决方案:

  1. 您可以添加公共构造函数并从测试中调用它。虽然它没有意义,但它也没有受到伤害(很多)。

  2. 创建一个虚拟静态实例(您可以在此处调用私有构造函数)。丑陋但你可以给这个字段一个名字来传达你的意图(JUST_TO_SILENCE_COBERTURA是一个好名字)。

  3. 您可以让测试扩展辅助类。这本质上会调用默认构造函数,但你的帮助程序类不能再是final

  4. 我建议最后一种方法,特别是因为课程不再是final。如果代码的使用者想要添加另一个辅助方法,他们现在可以扩展现有的类并接收一个句柄来获取所有辅助方法。这会创建一个辅助方法的耦合,它传达意图(这些属于一起) - 如果辅助类是final

    ,这是不可能的。

    如果您想阻止用户意外地实例化帮助程序类,请将其设为abstract而不是使用隐藏的构造函数。

答案 1 :(得分:29)

如果您绝对需要实现100%的代码覆盖率 - 其优点可以在别处争论:) - 您可以在测试中使用反射来实现它。作为习惯,当我实现一个静态实用程序类时,我添加了一个私有构造函数,以确保无法创建该类的实例。例如:

/** 
 * Constructs a new MyUtilities.
 * @throws InstantiationException
 */
private MyUtilities() throws InstantiationException
{
    throw new InstantiationException("Instances of this type are forbidden.");
}

然后你的测试看起来像这样:

@Test
public void Test_Constructor_Throws_Exception() throws IllegalAccessException, InstantiationException {
    final Class<?> cls = MyUtilties.class;
    final Constructor<?> c = cls.getDeclaredConstructors()[0];
    c.setAccessible(true);

    Throwable targetException = null;
    try {
        c.newInstance((Object[])null);
    } catch (InvocationTargetException ite) {
        targetException = ite.getTargetException();
    }

    assertNotNull(targetException);
    assertEquals(targetException.getClass(), InstantiationException.class);
}

基本上,你在这里做的是按类名获取类,在该类类型上查找构造函数,将其设置为public(setAccessible调用),不带参数调用构造函数,然后确保抛出的目标异常是InstantiationException

无论如何,正如你所说,这里100%的代码覆盖率要求是一种痛苦,但听起来它不在你手中,所以你无能为力。我实际上在我自己的代码中使用了类似于上面的方法,我确实发现它有益,但不是从测试的角度来看。相反,它只是帮助我学习了一些比我之前知道的更多的反思:)

答案 2 :(得分:7)

在所有情况下获得100%的覆盖率是好的,但在某些情况下这是不可能的。当然,如果你有一个从未实例化的类,Cobertura会把它作为一个不完整的测试覆盖,因为这些代码行实际上在类中,但它们没有经过测试。

事实上你永远不会调用私有构造函数(我假设你通过私有构造函数隐藏了构造函数),所以我不会打扰。测试应该是关于获得你期望的,虽然我同意100%的覆盖率是好的,在某些情况下(像这样),这是没用的。

同时查看100% Code Coverage

答案 3 :(得分:2)

没有

除非你明确地调用私有构造函数(这将是错误的代码),否则你将无法覆盖这些行。

答案 4 :(得分:0)

您可以跳过100%的覆盖率。它根本不会造成伤害。但是,如果您要开发非常严格的产品,并且将覆盖率定位为100%,则可以使用以下策略:

在您的情况下,构造函数缺少覆盖范围:您应该测试构造函数的预期行为。

如果未定义构造函数,则允许实例化该类(通过默认构造函数)。您应该测试默认的构造函数行为:在测试中,调用构造函数并测试结果,例如没有抛出错误,或者返回了一个有效的实例。

相反,如果作为实用程序类实践,您还定义了一个构造函数为private,并在构造函数中抛出UnsupportedOperationException。在测试中,您可以断言该行为。 :

public class HelperClassTest {
    @Test(expected = IllegalAccessException.class)
    public void ctorShouldBePrivate() throws InstantiationException, IllegalAccessException {
        HelperClass.class.newInstance();
    }

    @Test
    public void whenCtorIsCalledThroughReflectionUnsupportedOperationExceptionShouldBeThrown() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Constructor<HelperClass> constructor = HelperClass.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        try {
            constructor.newInstance();
            fail("Exception is expected");
        } catch (InvocationTargetException e) {
            assertThat(e.getCause(), is(instanceOf(UnsupportedOperationException.class)));
        }
    }
...
}

public class HelperClass{
    private HelperClass() {
        throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
    }
    ... 
}

答案 5 :(得分:0)

在我的案例中,龙目岛@UtilityClass可以将代码覆盖率提高到100%。