venable/software

Testing protected super method

When writing code you should generally prefer composition over inheritance. This approach is more flexible and sets your project up for changing behavior. However, there are a few cases where you will use inheritance. Sometimes you want to use the template method pattern because it very much fits a problem at hand. More often you are using code from another library or project and must comply with what you’ve been given.

A common pattern in software development is to write an override method that calls a super method. It may have form similar to the following, though there are a number of variations.

@Override
String getSomeValue(String input) {
    if(input.startsWith("test value")) {
        return "overriding " + input;
    }
    return super.getSomeValue(input);
}

When it comes time to test this method, you don’t want to have to write your test to depend on the logic within the super class. If you do, your tests rely on the logic within the superclass which exposes it to failure as the internal implementation changes. So instead you should mock the super class. But this is not as straightforward as testing code that uses composition.

The difficulty with mocking frameworks like Mockito is that you must access a member to mock it. This is normally a very nice feature because it lets us write mocking that also includes compile-time checking. But when the member is inaccessible, it poses a problem.

Thankfully, JUnit has a useful class to help with this: ReflectionSupport.

This tool allows us to write code to invoke the method we need to mock. Doing so will also

ReflectionSupport.invokeMethod(
  JakartaValidationModule.class.getDeclaredMethod("isNullable", MemberScope.class),
  doReturn(isNullable).when((JakartaValidationModule) objectUnderTest),
  memberScope);

Let’s break this example down. In this example, we have a subclass of JakartaValidationModule. This subclass is our object under test. We are testing a method which calls the protected isNullable method and want to mock isNullable. This first significant line gets the method we are looking for: JakartaValidationModule.class.getDeclaredMethod("isNullable", MemberScope.class). This is all using standard Java reflection APIs to get the method.

The next line mocks the method. doReturn(isNullable).when((JakartaValidationModule) objectUnderTest) You need to be sure to cast the objectUnderTest back to the base class in order to mock the base class' method.

The full sample is available in Data Prepper PR #5051.


Share