如何在Java应用程序中testing私有构造函数?
如果一个类包含一堆静态方法,为了确保没有人错误地初始化这个类的一个实例,我做了一个私有的构造函数:
private Utils() { }
现在..鉴于构造函数不能被看到,怎么能testing呢? 这可以testing覆盖呢?
使用reflection,你可以调用一个私有的构造函数:
Constructor<Util> c = Utils.class.getDeclaredConstructor(); c.setAccessible(true); Utils u = c.newInstance(); // Hello sailor
但是,你甚至可以做到这一点是不可能的:
private Utils() { throw new UnsupportedOperationException(); }
通过在构造函数中抛出exception,可以防止所有的尝试。
我也会让课程本身也是final
,只是“因为”:
public final class Utils { private Utils() { throw new UnsupportedOperationException(); } }
testing代码的意图..总是:)
例如:如果构造函数的要点是不可见的,那么你需要testing的是这个事实,没有别的。
使用reflectionAPI查询构造函数并validation它们是否具有私有属性集。
我会做这样的事情:
@Test() public void testPrivateConstructors() { final Constructor<?>[] constructors = Utils.class.getDeclaredConstructors(); for (Constructor<?> constructor : constructors) { assertTrue(Modifier.isPrivate(constructor.getModifiers())); } }
如果你想对对象构造进行适当的testing,你应该testing公共API,它允许你获得构造的对象。 这就是所说的API应该存在的原因:正确地build立对象,所以你应该testing它:)。
@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); }
以确保没有人错误地初始化这个类的一个实例
通常我所做的是将方法/构造函数从私有方法更改为默认的包可见性。 而且我对testing类使用相同的包,所以从testing中可以访问方法/构造函数,即使它不是来自外部的。
为了执行策略,不要实例化类,您可以:
- 抛出UnsupportedOperationException(“不要实例化这个类!”)从默认的空构造函数。
- 声明类的抽象:如果它只包含静态方法,你可以调用静态方法但不实例化它,除非你inheritance它。
或者同时应用1 + 2,如果您的testing与目标类共享相同的包,您仍然可以inheritance子类并运行构造函数。 这应该是相当“错误的证据”; 恶意代码将总是find解决方法:)
如果你有一个私有的构造函数,它是从你的代码的一些不那么私有的方法调用的。 所以你testing这个方法,并且你的构造函数被覆盖了。 按照每种方法进行testing没有宗教美德。 你正在寻找function或更好的分支覆盖水平,你可以简单地通过使用它的代码path行使构造函数。
如果代码path复杂且难以testing,则可能需要重构它。
如果在构造函数中添加一个exception,例如:
private Utils() { throw new UnsupportedOperationException(); }
在testing类中constructor.newInstance()
的InvocationTargetException
将会抛出一个InvocationTargetException
而不是你的UnsupportedOperationException
exception,但是所需的exception将被包含在抛出的exception中。
如果要断言抛出exception,则可以在调用exception被捕获后抛出调用exception的目标。
例如,使用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(); } }
别。 构造函数是私有的。 这就是你所需要的。 Java强制隐私。
不要testing平台。