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

问题描述 投票:32回答:5

我正在使用JUnit4和Cobertura仅使用静态方法测试助手类。测试方法是一项容易的任务,并且已经完成。

但是,cobertura显示该类没有被测试完全覆盖,因为它没有在任何地方实例化。

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

然后,cobertura抱怨空的私有构造函数未被测试覆盖。

是否有解决方案可以在这种情况下实现100%的代码覆盖率?

需要高层管理人员覆盖代码(在这种情况下,所以对于我来说,获得100%的特殊课程对我很有帮助。

java junit cobertura
5个回答
31
投票

有几种解决方案:

  1. 您可以添加一个公共构造函数并从测试中调用它。尽管这没有任何意义,但也不会造成太大的伤害。

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

  3. 您可以让您的测试extend帮助器类。这将本质上调用默认构造函数,但您的帮助器类将不再是final

我建议使用最后一种方法,特别是因为该类不再是final。如果您的代码使用者希望添加另一个帮助程序方法,则他们现在可以扩展现有的类,并获得一个获取所有帮助程序方法的句柄。这将创建一个用于传达意图(它们属于一起)的辅助方法的耦合-如果辅助类为final

是不可能的

如果要防止用户意外地实例化助手类,请将其设置为abstract,而不要使用隐藏的构造函数。


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%的代码覆盖率要求有点痛苦,但听起来好像是您无法控制的,因此您几乎无能为力。实际上,我在自己的代码中使用了与上述类似的方法,但确实发现了它的好处,但从测试的角度来看并没有。相反,它只是帮助我比以前了解了更多有关反射的知识:)


7
投票

在所有情况下都达到100%的覆盖率是好的,但是在某些情况下这是不可能的。当然,如果您有一个永不实例化的类,那么Cobertura会将其作为未完整的测试覆盖而获得,因为这些代码行实际上在该类中,但未经过测试。

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

也查看100% Code Coverage


2
投票

编号

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


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");
    }
    ... 
}
© www.soinside.com 2019 - 2024. All rights reserved.