如何在Java应用程序中测试私有构造函数?

时间:2012-12-29 01:20:24

标签: java unit-testing junit

如果一个类包含一堆静态方法,为了确保没有人错误地初始化该类的实例,我创建了一个私有构造函数:

private Utils() {
}

现在..如果不能看到构造函数,怎么可以测试呢?这可以覆盖测试吗?

7 个答案:

答案 0 :(得分:68)

使用反射,可以调用私有构造函数:

Constructor<Util> c = Utils.class.getDeclaredConstructor();
c.setAccessible(true);
Utils u = c.newInstance(); // Hello sailor

然而,你甚至可以做到这一点:

private Utils() {
    throw new UnsupportedOperationException();
}

通过在构造函数中抛出异常,可以防止所有尝试。


我也会让班级本身final,只是“因为”:

public final class Utils {
    private Utils() {
        throw new UnsupportedOperationException();
    }
}

答案 1 :(得分:19)

测试代码的意图..总是:)

例如:如果构造函数的私有点是不可见的,那么你需要测试的是这个事实,而不是别的。

使用反射API查询构造函数并验证它们是否具有私有属性集。

我会做这样的事情:

@Test()
public void testPrivateConstructors() {
    final Constructor<?>[] constructors = Utils.class.getDeclaredConstructors();
    for (Constructor<?> constructor : constructors) {
        assertTrue(Modifier.isPrivate(constructor.getModifiers()));
    }
}

如果要对对象构造进行适当的测试,则应测试允许您获取构造对象的公共API。这就是所述API应该存在的原因:正确构建对象所以你应该测试它:)。

答案 2 :(得分:6)

@Test
public//
void privateConstructorTest() throws Exception {
    final Constructor<?>[] constructors = Utils.class.getDeclaredConstructors();
    // check that all constructors are 'private':
    for (final Constructor<?> constructor : constructors) {
        Assert.assertTrue(Modifier.isPrivate(constructor.getModifiers()));
    }        
    // call the private constructor:
    constructors[0].setAccessible(true);
    constructors[0].newInstance((Object[]) null);
}

答案 3 :(得分:5)

  

确保没有人错误地初始化此类的实例

通常我所做的是将方法/构造函数从私有更改为默认包可见性。我对我的测试类使用相同的包,因此从测试中可以访问方法/构造函数,即使它不是来自外部。

要强制执行策略以不实例化该类,您可以:

  1. 从默认的空构造函数中抛出UnsupportedOperationException(“不要实例化此类!”)。
  2. 声明类抽象:如果它只包含静态方法,则可以调用静态方法但不实例化它,除非您将其子类化。
  3. 或同时应用1 + 2,如果您的测试与目标类共享相同的包,您仍然可以继承并运行构造函数。 这应该是非常“错误证明”;恶意程序员总能找到解决方法:)

答案 4 :(得分:4)

如果你有一个私有构造函数,它是从你的代码的一些不那么私有的方法调用的。因此,您测试该方法,并涵盖您的构造函数。对每种方法进行测试没有宗教信仰。您正在寻找功能或更好的分支覆盖级别,您只需通过使用它的代码路径来运行构造函数即可实现。

如果该代码路径错综复杂且难以测试,那么您可能需要对其进行重构。

答案 5 :(得分:3)

如果在构造函数中添加例外,例如:

private Utils() {
    throw new UnsupportedOperationException();
}

在测试类中调用constructor.newInstance()会抛出InvocationTargetException而不是UnsupportedOperationException,但所需的异常将包含在抛出的异常中。
如果要声明抛出你的异常,一旦捕获了调用异常,就可以抛出调用异常的目标。
例如,使用jUnit 4可以这样做:

@Test(expected = UnsupportedOperationException.class)
public void utilityClassTest() throws NoSuchMethodException, IllegalAccessException, InstantiationException {
    final Constructor<Utils> constructor = Utils.class.getDeclaredConstructor();
    constructor.setAccessible(true);
    try {
        constructor.newInstance();
    } catch (InvocationTargetException e) {
        throw (UnsupportedOperationException) e.getTargetException();
    }
}

答案 6 :(得分:-3)

别。构造函数是私有的。这就是你所需要的。 Java强制实施其隐私。

不要测试平台。