Search code examples
javaandroidmockitofinaljunit3

Is it possible to change the value of a final field in Java for the purposes of testing?


I think I already know the answer to this one, but it can't hurt to ask.

I have a class that handles requests to an external service. The class has a field that helps it stay within the service rate limit:
public static final int REQUEST_TIMEFRAME_MILLISECONDS = 600000;
Since this value is enforced by the service, and it is unlikely to change, I marked it as final so that it couldn't be over-ridden, and any manual change to them would give pause for thought.

It all works fine, until I start testing. Since the value is set at 10 minutes, it causes the test to run for much longer than I would like. Is there any way that I can use the class as normal, but with a lower value, for the purposes of the test? Or will I need to refactor it into a protected static int that can be changed through a sub-class?
It's part of an Android app and I'm using Junit3 with Mockito for testing, if that makes any difference.


Solution

  • rpg711 linked above to a way to change a field using reflection, but I'd advise against it. Bear in mind that Java is allowed to inline static final fields per JLS 13.4.9:

    If a field is a constant variable (§4.12.4), then deleting the keyword final or changing its value will not break compatibility with pre-existing binaries by causing them not to run, but they will not see any new value for the usage of the field unless they are recompiled. This is true even if the usage itself is not a compile-time constant expression (§15.28)

    It's also mentioned as a warning in JLS 17.5.3, as in the linked answer.

    Beyond that, also remember that someone (perhaps a future you) will be reading the code someday and make the (very fair) assumption that the static final int value will never change. You may be only changing it in the test, but that's still changing it, and you may be surprised at the behavior the next time you have to figure out why your test broke.


    While you can relax the modifiers to make it a non-final protected static int, I prefer either the "override for testing" or "overload for testing" method, depending on whether you like subclasses or visible-for-testing methods. Override-for-testing would look like this:

    public class YourComponent {
      protected int getRequestTimeframeMilliseconds() {
        return 600000;
      }
    }
    
    public class YourComponentTest {
      private static class YourComponentForTesting extends YourComponent {
        @Override protected int getRequestTimeframeMilliseconds() {
          return 500;
        }
      }
    
      @Test public void shouldTimeout() { /* ... */ }
    }
    

    ...which is actually pretty efficient, because the JVM can inline the method. Overload-for-testing would look like this:

    public class YourComponent {
      public static final int DEFAULT_REQUEST_TIMEFRAME_MILLISECONDS = 600000;
    
      public ReturnObject callService() {
        return callService(DEFAULT_REQUEST_TIMEFRAME_MILLISECONDS);
      }
    
      /** Package-private for testing. */
      ReturnObject callService(int timeframe) {
        /* Your implementation here. */
      }
    }
    

    The former can be nice if you don't mind the override, but the latter is particularly handy if you're a fan of dependency injection. Both benefit from package-private visibility if you put your system-under-test and test in the same package (even if they're in different source trees). Note that either way, you give the API an obvious and easy default, but you make it clear that there's a way to get different behavior. Tests are consumers of your components, and letting them modify behavior through the API (instead of through a back door) can help you define the behavior of your components.