June 10, 2015

Mocking properties of super classes in different packages for unit testing

Suppose I have the following reusable abstract class.
package org.ferris.io;
import org.apache.log4j.Logger;  
import javax.inject.Inject;
public abstract class AbstractPropertiesFile extends File {
  @Inject
  protected Logger log; 
  // . . .
}
Also suppose my application has a concrete implementation of this abstract class.
package org.ferris.application.preferences;
import  org.ferris.io.AbstractPropertiesFile;
public class PreferencesPropertiesFile extends AbstractPropertiesFile{
// . . .
} 
When unit testing PreferencesPropertiesFile, the challenge is to mock the protected Logger property of the super class.  It's a challenge because:
  1. The concrete class in in a different package than the abstract class
  2. The Logger property is being injected by CDI for you so there is no setter method.
Let's look at the first challenge.  A possible solution is to repackage my application and unit test to use the org.ferris.io package, but this means moving the PreferencesPropertiesFile to a package which doesn't make much sense for it to be in. Now let's look at the second challenge.  Adding a setter method would solve it, but do you really want to add a method just for unit testing purposes?  Neither of these solutions seem right.

A better solution is for the unit test to set the property of the super class using reflection. Though I'm sure many would argue against this solution, I'm OK with it because let's face it, every framework nowadays is using reflection or byte code manipulation, or proxying.  So a little more won't hurt.

Here is how you would setup the unit test.
@Mock
Logger logMock;
// . . .
@Test
public void someTest() {
    PreferencesPropertiesFile props = new PreferencesPropertiesFile ();
    {
        Field logField = AbstractPropertiesFile.class.getDeclaredField("log");
        logField.setAccessible(true);
        logField.set(props, logMock);
    }
    // . . .
} 
Enjoy!

No comments:

Post a Comment