0

Basically I'm trying to mock a static class for unit testing.

Example:

class Library {
    public static boolean isActivated() {
        return false;
    }
}

class Test {
    public static void main(String[] args) {
        // some magic
        
        if (Library.isActivated()) {
            System.out.println("'some magic' works!");
        } else {
            System.out.println("fail");
        }
    }
}

What would some magic need to be, to make Library.isActivated() return true.

Is there some special way to do this without changing the source code of Library?

As far as I know this is not possible in java but I'm not very familiar with Reflection I thought this might be possible with it.

Progman
  • 16,827
  • 6
  • 33
  • 48
Gian Laager
  • 474
  • 4
  • 14

2 Answers2

1

You definitely cannot override a static method. However, you can mock a static method with a lot of work. Generally, mocking a static method isn't a good sign. Fortunately, there are libraries out there that can do this if you need it.

Mockito has added support for this and there is a pretty good tutorial from Baeldung demonstrating how to do this: https://www.baeldung.com/mockito-mock-static-methods#mocking-a-no-argument-static-method.

Based on the tutorial:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>4.2.0</version>
    <scope>test</scope>
</dependency>
@Test
void mockLibraryIsActivated() {

    // Before mocking the static method is going to return false
    assertFalse(Library.isActivated());

    // Using MockedStatic to mock the method
    try (MockedStatic<Library> utilities = Mockito.mockStatic(Library.class)) {
        utilities.when(Library::isActivated).thenReturn(true);

        // Perform your test, the mock only works within the try block
        assertTrue(Library.isActivated());
    }

    // Mock is no longer active
    assertFalse(Library.isActivated());
}

The one key limitation to this is that the mock only applies within the code block it is defined in. So if you had another unit test you would also have to mock it within that unit test.

If you really want to dive down the rabbit hole you can look at how MockitoCore.mockStatic is implemented in the GitHub repo: https://github.com/mockito/mockito/blob/main/src/main/java/org/mockito/internal/MockitoCore.java.

keats
  • 26
  • 3
  • The library was not made with unit testing in mind. Would you also have the gradle version of the dependency? – Gian Laager Jan 20 '22 at 09:19
  • You can always go to Maven central to get the gradle/maven/sbt etc. version for a dependency: https://mvnrepository.com/artifact/org.mockito/mockito-core/4.2.0. You'll find it there as well as any later versions. – keats Jan 20 '22 at 14:06
0

Static methods are not virtual, so they cannot be overridden. Overloading would not serve your purpose, as it involves methods with the same name but different argument types, and in any case, you would need to modify class Library to add an overload. The Java language does not provide a means for a different class to impose an overload on the existing Library class.

Overall, this is reflective of broader issues with reliance on static methods and non-final static data.

Is there some special way to do this without changing the source code of Library?

As far as I know this is not possible in java but I'm not very familiar with Reflection I thought this might be possible with it.

Reflection does not do it, either. What you need to do is load a different version of the class than the one you show. That could conceivably be achieved by

  • careful management of the classpath
  • on-the-fly synthesis of a different version of Library
  • playing games with ClassLoaders

If you want to limit the scope of the replacement class -- so that your replacement is not also used in other tests -- then you probably need to exercise the ClassLoader option, though that is not necessarily exclusive of the the other two. This is non-trivial.

You may, however, be able to find a mocking library that can handle all of that for you.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157