JUnit:仅使用静态方法测试帮助程序类

2022-09-01 05:48:28

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

但是,cobertura 表明该类未完全被测试覆盖,因为它未在任何地方实例化。

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

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

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

顶级管理(在本例中)需要代码覆盖率,因此对我来说,为这个特定类获得100%是非常有帮助的。


答案 1

有几种解决方案:

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

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

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

我建议最后一种方法,特别是因为类不能再了。如果代码的使用者想要添加另一个帮助器方法,他们现在可以扩展现有类并接收一个句柄来访问所有帮助器方法。这创建了传达意图的帮助器方法的耦合(这些方法属于一起) - 如果帮助器类是finalfinal

如果要防止用户意外实例化帮助程序类,请使其而不是使用隐藏的构造函数。abstract


答案 2

如果你绝对需要实现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(调用),调用不带参数的构造函数,然后确保引发的目标异常是 .setAccessibleInstantiationException

无论如何,正如你所说,这里的100%代码覆盖率要求有点痛苦,但听起来好像它超出了你的控制范围,所以你对此无能为力。我实际上在我自己的代码中使用了与上述类似的方法,我确实发现它是有益的,但不是从测试的角度来看。相反,它只是帮助我学到了比我之前所知道的更多的反思:)


推荐