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.
CodePudding user response:
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.
CodePudding user response:
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.
