loading...

Mocking private static final log

moaxcp profile image John Mercier ・2 min read

Tests for logging is something I have tried to avoid in the past. Today I had to write some code which exports TestRail results to splunk and I was determined to test the output.

Instead of searching for how to do this using jmockit or mockito (since this hasn't worked for me in the past) I decided to try using reflection myself. I searched for "java reflection change private static field" and came across this post on stackoverflow. The post shows a way to change the value of a public static final field. There are some caveats but in the case of Logger it works out.

Combining this information with mockito I was able to mock the logger for a class and test logging output. First the test setup (junit 4.13):

@RunWith(MockitoJunitRunner.class)
public class SplunkReporterMethods {
  @Mock private Logger log;
  private SplunkReporter reporter;

  @Test
  public void report() {
    reporter.report(new Car());
    then(log).should().info("{\"name\":\"car\",\"speed\":4}");
  }
}

To setup the SplunkReporter class with a different logger the mock needs to be injected into the private static final field. This can be done in an @Before method.

@Before
void setup() {
  //allow log field to be changed
  Field field = SplunkReporter.class.getDeclaredField("log");
  field.setAccessible(true);

  //remove final modifier
  Field modifiersField = Field.class.getDeclaredField("modifiers");
  modifiersField.setAccessible(true);
  modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);

  //set to mock object
  field.set(null, log);
  reporter = new SplunkReporter();
}

First, the setup method makes log accessible but that is not enough. The modifiers on the field need to be changed so final is removed. The Field class does not have a setter for the modifiers field and so modifiers needs to be set in field using reflection. Finally the static field is set to the mock object.

Discussion

markdown guide