JUnit: testing helper class with only static methods
Asked Answered
R

7

37

I am testing a helper class with only static methods with JUnit4 and Cobertura. Testing methods was easy task and is done already.

However, cobertura shows that the class is not covered by tests completely, as it is not instantiated anywhere.

I don't want to create an instance of this class (it is a helper class), so first solution is to hide constructor (which is generally good approach for helper class).

Then cobertura complains that the empty private constructor is not covered by tests.

Is there any solution to achieve 100% code coverage for such situation?

Code coverage is required from top level management (in this case), so for me obtaining 100% for this particular class is quite helpful.

Rikki answered 14/3, 2012 at 10:31 Comment(0)
H
36

There are several solutions:

  1. You can add a public constructor and call it from a test. While it doesn't make sense, it also doesn't hurt (much).

  2. Create a dummy static instance (you can call the private constructor here). Ugly but you can give the field a name to communicate your intent (JUST_TO_SILENCE_COBERTURA is a good name).

  3. You can let your test extend the helper class. That will intrinsically call the default constructor but your helper class can't be final anymore.

I suggest the last approach especially because the class can't be final anymore. If a consumer of your code wants to add another helper method, they can now extend the existing class and receive one handle to get to all helper methods. This creates a coupling of the helper methods which communicates the intent (these belong together) - which is impossible if the helper class is final

If you want to prevent users to accidentally instantiate the helper class, make it abstract instead of using a hidden constructor.

Horrific answered 14/3, 2012 at 10:42 Comment(2)
The third approach is in my opinion the best as well, cause it doesn't influence code at all (only tests). I would take this way. Thank you for help.Hawkeyed
You can make the class abstract, too.Smolt
B
30

If you absolutely need to achieve 100% code coverage - the merits of that can be debated elsewhere :) - you can achieve it using reflection in your tests. As habit, when I implement a static-only utility class, I add in a private constructor to ensure that instances of the class can't be created. For example:

/** 
 * Constructs a new MyUtilities.
 * @throws InstantiationException
 */
private MyUtilities() throws InstantiationException
{
    throw new InstantiationException("Instances of this type are forbidden.");
}

Then your test might look something like this:

@Test
public void Test_Constructor_Throws_Exception() throws IllegalAccessException, InstantiationException {
    final Class<?> cls = MyUtilties.class;
    final Constructor<?> c = cls.getDeclaredConstructors()[0];
    c.setAccessible(true);

    Throwable targetException = null;
    try {
        c.newInstance((Object[])null);
    } catch (InvocationTargetException ite) {
        targetException = ite.getTargetException();
    }

    assertNotNull(targetException);
    assertEquals(targetException.getClass(), InstantiationException.class);
}

Basically, what you're doing here is getting the class by name, finding the constructors on that class type, setting it to public (the setAccessible call), calling the constructor with no arguments, and then ensuring that the target exception that is thrown is an InstantiationException.

Anyway, as you said, the 100% code coverage requirement here is kind of a pain, but it sounds like it's out of your hands, so there's little you can do about it. I've actually used approaches similar to the above in my own code, and I did find it beneficial, but not from a testing perspective. Rather, it just helped me learn a little bit more about reflection than I knew before :)

Blink answered 14/3, 2012 at 12:33 Comment(2)
Why is a 'return' present in the catch block? The test will always return true. ;)Cantabile
fixed - 3+ years and no one noticed that :)Blink
A
7

Obtaining 100% coverage in all cases it's good, but there are some cases in which this is not possible. Of course if you have a class that is never instantiated, Cobertura will get this as a not complete test coverage, because those lines of code are actually in the class, but they're not tested.

Fact is you'll never call a private constructor (I'm assuming you've hidden the constructor by making it private), so I wouldn't bother. Test should be about getting what you're expecting, and while I agree 100% coverage is good, in some cases (like this) this isn't useful.

Have a look at 100% Code Coverage as well.

Amory answered 14/3, 2012 at 10:43 Comment(1)
Sometimes code coverage is required from top level management (in this case it is), so for me obtaining 100% for this particular class is quite helpful. And yes - I am aware of ridiculousness of this approach. However - thank you for link for article.Hawkeyed
B
2

No.

Unless you call the private constructor explicitly (which would be bad code) you won't be able to cover those lines.

Bases answered 14/3, 2012 at 10:38 Comment(0)
L
1

The lombok @UtilityClass worked in my case to improve the code coverage to be 100%.

Lignite answered 16/9, 2020 at 1:10 Comment(0)
P
0

You may skip 100% coverage for those. It should not harm at all. However, if you are working on a very strict product, targeting 100% coverage, then you may use the strategy below:

In your case, the missing coverage is the constructor: You should test the expected behavior of the constructor.

If you do not define a constructor, then you allow the class to be instantiated (through the default constructor). You should test the default constructor behavior: In the tests, call the constructor and test the results, e.g. no error is thrown, or a valid instance is returned.

If, instead, as a utility class practice, you also define a constructor as private and throw an UnsupportedOperationException in the constructor. In the tests, you may assert that behavior. :

public class HelperClassTest {
    @Test(expected = IllegalAccessException.class)
    public void ctorShouldBePrivate() throws InstantiationException, IllegalAccessException {
        HelperClass.class.newInstance();
    }

    @Test
    public void whenCtorIsCalledThroughReflectionUnsupportedOperationExceptionShouldBeThrown() throws InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Constructor<HelperClass> constructor = HelperClass.class.getDeclaredConstructor();
        constructor.setAccessible(true);
        try {
            constructor.newInstance();
            fail("Exception is expected");
        } catch (InvocationTargetException e) {
            assertThat(e.getCause(), is(instanceOf(UnsupportedOperationException.class)));
        }
    }
...
}

public class HelperClass{
    private HelperClass() {
        throw new UnsupportedOperationException("This is a utility class and cannot be instantiated");
    }
    ... 
}
Paletot answered 23/11, 2019 at 21:44 Comment(0)
E
0

On junit 5 (jupiter) when you add a private constructor to the class with static methods, the coverage is 100%

Eckart answered 30/6, 2022 at 15:15 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.