46

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

private Utils() {
}

现在.. 鉴于构造函数看不到,这怎么能被测试?这完全可以被测试覆盖吗?

4

7 回答 7

73

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

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();
    }
}
于 2012-12-29T01:25:40.033 回答
22

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

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

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

我会做这样的事情:

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

如果您想对对象构造进行适当的测试,您应该测试允许您获取构造对象的公共 API。这就是上述 API 应该存在的原因:正确构建对象,因此您应该对其进行测试:)。

于 2012-12-29T01:29:45.497 回答
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);
}
于 2012-12-29T01:23:40.170 回答
5

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

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

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

  1. 从默认的空构造函数中抛出 UnsupportedOperationException("不要实例化此类!")。
  2. 声明类抽象:如果它只包含静态方法,您可以调用静态方法但不能实例化它,除非您将其子类化。

或者同时应用 1+2,如果您的测试与目标类共享相同的包,您仍然可以子类化并运行构造函数。这应该是相当“防错”的;恶意编码人员总能找到解决方法:)

于 2012-12-29T01:46:46.717 回答
4

如果您有一个私有构造函数,它会从您的代码的一些不那么私有的方法中调用。所以你测试那个方法,你的构造函数就被覆盖了。根据方法进行测试没有宗教美德。您正在寻找函数或更好的分支覆盖级别,您可以通过使用它的代码路径执行构造函数来简单地获得它。

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

于 2012-12-29T01:34:21.430 回答
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();
    }
}
于 2015-08-11T09:39:41.927 回答
-4

不。构造函数是私有的。这就是你所需要的。Java 强制执行其隐私。

不要测试平台。

于 2015-08-11T09:44:07.617 回答