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

6ioyuze2  于 2022-11-11  发布在  其他
关注(0)|答案(7)|浏览(178)

我正在用JUnit4和Cobertura测试一个只有静态方法的helper类。测试方法很容易,而且已经完成了。
但是,cobertura表明类并没有被测试完全覆盖,因为它在任何地方都没有示例化。
我不想创建这个类的示例(它是一个帮助类),所以第一个解决方案是隐藏构造函数(这通常是帮助类的好方法)。
然后cobertura抱怨空的私有构造函数没有被测试覆盖。
对于这种情况,是否有任何解决方案可以实现100%的代码覆盖率?
代码覆盖率是顶层管理所必需的(在本例中),所以对我来说,获得这个特定类的100%覆盖率是非常有帮助的。

fumotvh3

fumotvh31#

有几种解决方案:
1.你可以添加一个公共构造函数,然后从测试中调用它。虽然这没有什么意义,但也没有什么坏处。
1.创建一个虚拟的静态示例(你可以在这里调用私有构造函数)。很难看,但是你可以给予字段一个名字来表达你的意图(JUST_TO_SILENCE_COBERTURA是一个好名字)。
1.你可以让你的测试扩展帮助类,这将内在地调用默认的构造函数,但是你的帮助类不能再是final
我建议使用最后一种方法,特别是因为类不能再是final。如果代码的消费者想添加另一个helper方法,他们现在可以扩展现有的类,并接收一个句柄来访问所有helper方法。这将创建一个helper方法的耦合,用于传达意图(这些方法属于一起)--如果helper类是final,这是不可能的
如果要防止用户意外地示例化helper类,请将其设置为abstract,而不是使用隐藏的构造函数。

crcmnpdw

crcmnpdw2#

如果你绝对需要实现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%代码覆盖率要求是一种痛苦,但它听起来似乎超出了您的控制范围,因此您对此无能为力。实际上,我在自己的代码中使用了类似于上述的方法,我确实发现它是有益的,但不是从测试的Angular 来看。相反,它只是帮助我比以前更多地了解了反射:)

holgip5t

holgip5t3#

在所有情况下都能获得100%的覆盖率是很好的,但是在某些情况下这是不可能的。当然,如果你有一个类从未被示例化,Cobertura会把它作为一个不完整的测试覆盖率,因为那些代码行实际上在类中,但是它们没有被测试。
事实上,你永远不会调用私有构造函数(我假设你已经通过将其设为私有来隐藏构造函数),所以我不会费心。测试应该是关于获得你所期望的,虽然我同意100%覆盖是好的,但在某些情况下(像这样)这是没有用的。
请同时查看100% Code Coverage

yebdmbv4

yebdmbv44#

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

6tdlim6h

6tdlim6h5#

lombok@UtilityClass在我的例子中将代码覆盖率提高到了100%。

njthzxwz

njthzxwz6#

你可以跳过100%的覆盖率。这应该不会有什么坏处。但是,如果你正在开发一个非常严格的产品,目标是100%的覆盖率,那么你可以使用下面的策略:
在您的示例中,缺少的覆盖率是构造函数:您应该测试建构函式的预期行为。
如果不定义构造函数,则允许示例化类(通过默认构造函数)。应测试默认构造函数行为:在测试中,呼叫建构函式并测试结果,例如,没有掷回错误,或传回有效的执行严修。
相反,如果作为实用程序类的实践,您还将构造函数定义为private,并在构造函数中抛出一个UnsupportedOperationException。在测试中,您可以Assert该行为。

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");
    }
    ... 
}
qkf9rpyu

qkf9rpyu7#

在junit 5(jupiter)上,当您使用静态方法向类添加私有构造函数时,覆盖率为100%。

相关问题