Want to start a religious discussion? Start asking your teammates whether or not you should mock static method calls. It's debatable if this is an absolute no-go or sometimes a last resort. I agree that you should rethink your implementation if you find yourself googling: Mocking static calls Java. On the other side, there are still valid use cases where this approach is considerable. In the past, PowerMock was the most popular solution for this problem in Java. With version 3.4.0 Mockito now also supports mocking static methods.
PS: Before arguing with your co-workers, read through the different opinions around mocking static methods at the corresponding GitHub issue from Mockito.
Required Mockito setup
Mocking static methods is part of Mockito since version 3.4.0. If you are using Maven either update your existing Mockito version or include the following dependency to your pom.xml
:
1 2 3 4 5 6 7 | <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <!-- version needs to be >= 3.4.0 --> <version>3.5.13</version> <scope>test</scope> </dependency> |
If your project uses mockito-core
, you'll see the following exception and should replace it with mockito-inline
.
1 2 3 4 5 6 | org.mockito.exceptions.base.MockitoException: The used MockMaker SubclassByteBuddyMockMaker does not support the creation of static mocks Mockito's inline mock maker supports static mocks based on the Instrumentation API. You can simply enable this mock mode, by placing the 'mockito-inline' artifact where you are currently using 'mockito-core'. Note that Mockito's inline mock maker is not supported on Android. |
For those of you that use Spring Boot and the Spring Boot Starter Test, you can update to Spring Boot Version 2.4.0-M2. This version includes the Mocktio dependency in a compatible version (> 3.4.0).
If your project can't update the main Spring Boot version (yet), you can manually override the used Mockito version using:
1 2 3 | <properties> <mockito.version>3.5.13</mockito.version> </properties> |
And include mockito-inline
if required:
1 2 3 4 5 | <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-inline</artifactId> <scope>test</scope> </dependency> |
Mocking static methods with Java
Let's take a look at how to use this feature for a Java method that accesses two static methods: UUID.randomUUID()
and LocalDateTime.now()
.
Whether or not this implementation or the corresponding tests make sense is not up for discussion. The example is only for demonstration purposes on how to mock static method calls with Mockito.
1 2 3 4 5 6 7 8 9 10 11 12 13 | public class OrderService { public Order createOrder(String productName, Long amount, String parentOrderId) { Order order = new Order(); order.setId(parentOrderId == null ? UUID.randomUUID().toString() : parentOrderId); order.setCreationDate(LocalDateTime.now()); order.setAmount(amount); order.setProductName(productName); return order; } } |
With a first test, we want to make sure to use a random UUID
whenever we don't pass a parentOrderId
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | class OrderServiceTest { private OrderService cut = new OrderService(); private UUID defaultUuid = UUID.fromString("8d8b30e3-de52-4f1c-a71c-9905a8043dac"); private LocalDateTime defaultLocalDateTime = LocalDateTime.of(2020, 1, 1, 12, 0); @Test void shouldIncludeRandomOrderIdWhenNoParentOrderExists() { try (MockedStatic<UUID> mockedUuid = Mockito.mockStatic(UUID.class)) { mockedUuid.when(UUID::randomUUID).thenReturn(defaultUuid); Order result = cut.createOrder("MacBook Pro", 2L, null); assertEquals("8d8b30e3-de52-4f1c-a71c-9905a8043dac", result.getId()); } // ... next test } |
Here we are using a try-with-resources statement to create a mocked version of the UUID
class. This is due to the fact that MockedStatic
extends the ScopedMock
interface which itself extends AutoClosable
. The static mocks are thread-local scoped and hence need to be closed.
Inside the try-with-resources statement, we then get access to the mocked version of UUID
and can define its behavior using Mockito's well-known when().thenReturn()
stubbing setup.
As an alternative, we could also manually call .close()
inside @AfterEach
.
The same is true when we write a test that includes mocking LocalDateTime.now()
:
1 2 3 4 5 6 7 8 9 10 | @Test void shouldIncludeCurrentTimeWhenCreatingANewOrder() { try (MockedStatic<LocalDateTime> mockedLocalDateTime = Mockito.mockStatic(LocalDateTime.class)) { mockedLocalDateTime.when(LocalDateTime::now).thenReturn(defaultLocalDateTime); Order result = cut.createOrder("MacBook Pro", 2L, "42"); assertEquals(defaultLocalDateTime, result.getCreationDate()); } } |
Mocking a static utility method that takes arguments
Both mocking examples above were using Java's method reference. That's a convenient way to write compact lambda expressions by referring to an existing method. However, this doesn't apply to mocking every static method.
Let's use the following MyUtils
class as an example:
1 2 3 4 5 6 7 8 9 10 | public class MyUtils { public static String getWelcomeMessage(String username, boolean isCustomer) { if (isCustomer) { return "Dear " + username; } else { return "Hello " + username; } } } |
When we now want to mock the static .getWelcomeMessage()
method, we can't use the method reference. Mockito's .when()
takes a Verification
as an argument. This is a functional interface as it has one public method: .apply()
.
Hence for both UUID.randomUUID()
and LocalDateTime.now()
we could use their method reference like.when(LocalDateTime::now)
.
That's not possible for .getWelcomeMessage(String username, boolean isCustomer)
. If we want to mock this utility method, we can write a custom lambda expression to provide a Verification
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class MyUtilsTest { @Test void shouldMockStaticMethod() { try (MockedStatic<MyUtils> mockedStatic = Mockito.mockStatic(MyUtils.class)) { mockedStatic .when(() -> MyUtils.getWelcomeMessage(eq("duke"), anyBoolean())) .thenReturn("Howdy duke!"); String result = MyUtils.getWelcomeMessage("duke", false); assertEquals("Howdy duke!", result); } } } |
Mocking static methods with Kotlin and Mockito
Let's take a look at the OrderService
again, but this time we use Kotlin to implement its functionality:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | class OrderService { fun createOrder(productName: String, amount: Long, parentOrderId: String?) = Order( id = parentOrderId ?: UUID.randomUUID().toString(), creationDateTime = LocalDateTime.now(), amount = amount, productName = productName ) } data class Order( val productName: String, val amount: Long, val id: String, val creationDateTime: LocalDateTime ) |
Mocking the static method calls also works here. To write even more idiomatic Kotlin code, we can replace the try-with-resources statement with the use
function of the Kotlin standard library:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | class OrderServiceKtTest { private val cut = OrderService() private val defaultUuid = UUID.fromString("8d8b30e3-de52-4f1c-a71c-9905a8043dac") @Test fun `should include random order id when no parent order exists`() { Mockito.mockStatic(UUID::class.java).use { mockedUuid -> mockedUuid.`when`<Any> { UUID.randomUUID() }.thenReturn(defaultUuid) val result = cut.createOrder("MacBook Pro", 2L, null) assertEquals("8d8b30e3-de52-4f1c-a71c-9905a8043dac", result.id) } } } |
Due to the fact that when
is a reserved keyword in Kotlin, we have to use the backticks when stubbing the behavior.
Both mockito-kotlin and Mockk seem not to support this yet.
Refactored alternative to avoid mocking static methods
As an alternative, let's have a look at how we can avoid mocking static methods for our OrderService
. If our project uses a framework that supports dependency injection (e.g. Spring, Jakarta EE with CDI, MicroProfile, Guice), we can slightly refactor our current implementation:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public class OrderServiceRefactored { private final Clock clock; private final OrderIdGenerator orderIdGenerator; public OrderServiceRefactored(Clock clock, OrderIdGenerator orderIdGenerator) { this.clock = clock; this.orderIdGenerator = orderIdGenerator; } public Order createOrder(String productName, Long amount, String parentOrderId) { Order order = new Order(); order.setId(parentOrderId == null ? orderIdGenerator.generateOrderId() : parentOrderId); order.setCreationDate(LocalDateTime.now(clock)); order.setAmount(amount); order.setProductName(productName); return order; } } |
With this approach, we outsource the creation of the orderId
to another component. In addition to this, we make use of an overloaded version of LocalDateTime.now()
that takes a Clock
to request the current date & time.
This small refactoring allows us to mock both the Clock
and OrderIdGenerator
while unit testing our OrderServiceRefactored
class:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | class OrderServiceRefactoredTest { private OrderIdGenerator orderIdGenerator = mock(OrderIdGenerator.class); private Clock clock = mock(Clock.class); private OrderServiceRefactored cut = new OrderServiceRefactored(clock, orderIdGenerator); @Test void shouldIncludeRandomIdAndCurrentDateTime() { when(orderIdGenerator.generateOrderId()).thenReturn("8d8b30e3-de52-4f1c-a71c-9905a8043dac"); LocalDateTime defaultLocalDateTime = LocalDateTime.of(2020, 1, 1, 12, 0); Clock fixedClock = Clock.fixed(defaultLocalDateTime.toInstant(ZoneOffset.UTC), ZoneId.of("UTC")); when(clock.instant()).thenReturn(fixedClock.instant()); when(clock.getZone()).thenReturn(fixedClock.getZone()); Order result = cut.createOrder("MacBook Pro", 2L, null); assertEquals("8d8b30e3-de52-4f1c-a71c-9905a8043dac", result.getId()); assertEquals(defaultLocalDateTime, result.getCreationDate()); } } |
We can now use standard Mockito functionality to mock the behavior of our external components during the test.
Remember: Having access to a tool does not mean you should always use it.
The refactored example should give you valuable hints on how to work around mocking static calls. If there is still the need to mock them, you don't need to include PowerMock, as Mockito now supports it.
Are you looking for further practical resources on Mockito? Consider enrolling in my Hands-On With Mockito Online Course to learn the ins and outs of the most popular mocking library for JVM applications.
You can find the source code for this Mockito example on Github.
Have fun mocking static method calls with Mockito,
Philip
[…] though Mockito is able to mock static method calls and with deep mocking, we could keep the stubbing setup lean, we should always […]
Gives error –
org.opentest4j.AssertionFailedError:
Expected :8d8b30e3-de52-4f1c-a71c-9905a8043dac
Actual :null-null-null-null-null
How to solve?
Hi Abel,
what exactly did you try to run?
Hello and thanks for the example.
I am interested in mocking multiple static methods for the same test, using the aforementioned technique. Is there a better way to do it than using a try inside of a try?
Hi bilbo,
you can put multiple declarations inside one try block, like
mockedUuid = Mockito.mockStatic(UUID.class); mockedLocalDateTime = Mockito.mockStatic(LocalDateTime.class)) {
try (MockedStatic
MockedStatic
}
Let me know if that helps,
Philip
Thanks a lot for your answer rieckpil.
I already tried that. My problem is that i am doing multiple tests, in which i use the same mock of static methods, so i need in every test to use these declarations again.
So i was trying to find a way to declare these mocks in the beginning of the class, so i don’t use the same block of code again and again.
Thanx anyway for your help.
For this scenario you could define all your static mock as part of
@BeforeAll
(without anytry-with-resources
statement) and close them manually using.close()
inside@AfterAll
. It’s important to close them as they will otherwise remain active on the current thread.OK i will try that.
Thans Philip 🙂
Hey! May i ask if you know whether it is possible to mock as static also private methods, using Mockito?
Hi bilbo2,
it’s not possible to mock private methods with Mockito. You can find further information at this Stack Overflow question.
Kind regards,
Philip
return webClient.get()
.uri(kongAPIProperties.getCashAvailabilityUri(), positionId, transId1, transId2)
.attributes(clientRegistrationId(KONG_REGISTRATION_ID))
clientRegistrationId(KONG_REGISTRATION_ID)
is static method how to mock
Hi Ravi,
I’m happy to help – please create a question on Stack Overflow and provide a little bit more information about your setup and what you’ve already tried. You can reply with a link to the question and I’ll take a look at soon as possible 🙂
Kind regards,
Philip