Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JUnit: testing helper class with only static methods

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.

like image 735
Mateusz Chromiński Avatar asked Mar 14 '12 10:03

Mateusz Chromiński


People also ask

Are helper classes static?

In general, helper classes do not have to have all static methods, but may have instance variables. Multiple instances of the helper class may exist as well.

Should helper methods be static Java?

Helper methods are often declared as public static so they can be invoked with the class name like Classname.

How do you write a unit test case for static methods?

Note that you should add the class that contains static methods in two places in your unit tests: On top of the unit test class using @PrepareForTest annotation. In your test setup by calling the PowerMockito. mockStatic to do the necessary initialization step before trying to mock any of its methods.


2 Answers

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.

like image 142
Aaron Digulla Avatar answered Sep 19 '22 05:09

Aaron Digulla


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

like image 34
matt Avatar answered Sep 20 '22 05:09

matt