48

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?

Bruno
  • 119,590
  • 31
  • 270
  • 376
James Raitsev
  • 92,517
  • 154
  • 335
  • 470

7 Answers7

76

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();
    }
}
Bohemian
  • 412,405
  • 93
  • 575
  • 722
23

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 :).

sdasdadas
  • 23,917
  • 20
  • 63
  • 148
Mihai Toader
  • 12,041
  • 1
  • 29
  • 33
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);
}
MrSmith42
  • 9,961
  • 6
  • 38
  • 49
  • 1
    I use it to increase test coverage of my utils classes to 100% – MrSmith42 Dec 29 '12 at 01:24
  • 2
    @MrSmith42 Getting test coverage close to 100% is not a thing-in-itself. Test coverage is a metric. And any action intended solely to improve the metric rather then to achieve actual result devalues the metric. After all you can have all of your code covered by tests that exercise all methods but do not verify the results. So, it may make sense to test private constructor by directly calling it only if you have a strong reason to do so. (Better to test it indirectly if possible - by calling factory method or something like). Anyway, knowing how to do it if needed is good :-) – Alexey Tigarev May 17 '14 at 09:26
  • 2
    @Alexey Tigarev: If I have a utils class, where the constructor is never called, and all static methods are tested with 100%. 100% of the ever called methods are covered by test, so I like to have the coverage tool also to report 100% coverage for the class. If this is only possible by 'testing' the never used private constructor this way, I recommend to do so. – MrSmith42 May 17 '14 at 19:20
5

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:

  1. throw UnsupportedOperationException("don't instantiate this class!") from the default empty constructor.
  2. 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 :)

Luigi R. Viggiano
  • 8,659
  • 7
  • 53
  • 66
4

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.

bmargulies
  • 97,814
  • 39
  • 186
  • 310
4

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();
    }
}
Juan
  • 1,754
  • 1
  • 14
  • 22
-4

Don't. The constructor is private. That's all you need. Java enforces its privacy.

Don't test the platform.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • 2
    A practical use case to test otherwise inaccessible code is to achive 100% test coverage so that no one has to look at that class again. If the coverage is stuck at 95% many developers may attempt to figure out the reason for that just to bump into this issue over and over again. Also, Java sucks at enforcing privacy. :) – thisismydesign Feb 22 '17 at 15:43
  • @thisismydesign The stated purpose here is 'in order to make sure no one by mistake initializes an instance of this class'. Whatever `private` may or may not do, it is pointless to test its workings yourself. – user207421 Jun 25 '18 at 17:57
  • You're not just testing the inner workings of the private keyword, you're also testing that nobody ever changes it to public. – Ivo van der Veeken Feb 11 '19 at 15:44