可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
This question already has an answer here:
-
How to add test coverage to a private constructor?
16 answers
If a class contains a bunch of static methods, in order to make sure no one by mistake initializes an instance of this class, I made a private constructor:
private Utils() {
}
Now .. how could this be tested, given that constructor can't be seen? Can this be test covered at all?
回答1:
Using reflection, you can invoke a private constructor:
Constructor<Util> c = Utils.class.getDeclaredConstructor();
c.setAccessible(true);
Utils u = c.newInstance(); // Hello sailor
However, you can make even that not possible:
private Utils() {
throw new UnsupportedOperationException();
}
By throwing an exception in the constructor, you prevent all attempts.
I would make the class itself final
too, just "because":
public final class Utils {
private Utils() {
throw new UnsupportedOperationException();
}
}
回答2:
Test the intent of the code .. always :)
For example: If the point of the constructor being private is to not be seen then what you need to test is this fact and nothing else.
Use the reflection API to query for the constructors and validate that they have the private attribute set.
I would do something like this:
@Test()
public void testPrivateConstructors() {
final Constructor<?>[] constructors = Utils.class.getDeclaredConstructors();
for (Constructor<?> constructor : constructors) {
assertTrue(Modifier.isPrivate(constructor.getModifiers()));
}
}
If you want to have a proper test for the object construction, you should test the public API which allows you to get the constructed object. That's the reason the said API should exist: to build the objects properly so you should test it for that :).
回答3:
@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);
}
回答4:
to make sure no one by mistake initializes an instance of this class
Usually what I do, is to change the method/constructor from private to default package visibility. And I use the same package for my test class, so from the test the method/constructor is accessible, even if it is not from outside.
To enforce the policy to not instantiate the class you can:
- throw UnsupportedOperationException("don't instantiate this class!") from the default empty constructor.
- declare the class abstract: if it only contains static methods, you can call the static methods but not instantiate it, unless you subclass it.
or apply both 1+2, you can still subclass and run the constructor if your test shares the same package as the target class.
This should be quite "error proof"; malicious coders will always find a workaround :)
回答5:
If you have a private constructor, it is called from some not-so-private method of your code. So you test that method, and your constructor is covered. There's no religious virtue in having a test per method. You are looking for function or better yet branch coverage level, and you can get that simply by exercising the constructor through the code path that uses it.
If that code path is convoluted and hard to test, perhaps you need to refactor it.
回答6:
If you add an exception in the constructor such as:
private Utils() {
throw new UnsupportedOperationException();
}
The invocation of constructor.newInstance()
in the test class will throw an InvocationTargetException
instead of your UnsupportedOperationException
, but the desired exception will be contained in the thrown one.
If you want to assert the thrown of your exception, you could throw the target of the invocation exception, once the invocation exception has been caught.
For instance, using jUnit 4 you could do this:
@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();
}
}
回答7:
Don't. The constructor is private. That's all you need. Java enforces its privacy.
Don't test the platform.