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. Starting with version 3.4.0, Mockito now 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 Maven Setup
Mocking static methods is part of Mockito since version 3.4.0.
If we are using Maven, we can either update our 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>4.6.1</version> <scope>test</scope> </dependency> |
If our project uses mockito-core
, we'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. |
When developing an application with Spring Boot and the Spring Boot Starter Test, we can update to Spring Boot Version 2.4.0-M2. This version includes the Mocktio dependency in a compatible version (> 3.4.0).
If our project can't update the main Spring Boot version (yet), we can manually override the used Mockito version using:
1 2 3 | <properties> <mockito.version>4.6.1</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> |
Update: Starting with Mockito version 5.0.0, the mockito-inline
is now the default MockMaker
. If we're using this version, we can skip any manual configuration to use the InlineMockMaker
.
Important: The upcoming code examples use Java 11. Not everything will work with Java 8 as expected, as the internals of UUID.toString()
did change between those two Java releases.
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 the 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 because 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 Static Methods With Method 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) } } } |
Since 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 use 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 related Mockito articles here:
- Mock Java Constructors With Mockito | Configuration and Examples
- @Mock vs. @MockBean When Testing Spring Boot Applications
- Maven Setup For Testing Java Applications
The source code for this Mockito example is available 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?
I’m getting this too
I have tried this tutorial code.
Also it works fine with LocalDateTime
Hi Mahmoud,
can you upload a minimal reproducible example to GitHub and send me the link? Without any further information, it’s quite hard to help.
You can also check the source code for this blog post on GitHub where the tests are constantly executed and green.
Kind regards,
Philip
Hi Philip,
After investigation this is because I’m using JDK 1.8 after upgrading to JDK 11 it runs without issues
Hi Mahmoud,
thanks for coming back and reporting what fixed your issue. I’ll take a look at the example with JDK 1.8
As a short update: The implementation of
UUID.toString()
changed between Java 1.8 and 11. With Java 1.8. thetoString()
method uses another method of UUID which returns null when we statically mock the entire class. With Java 11 it doesn’t use any internals anymore and hence we can safely calltoString()
of the mocked UUID class.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
public class KongAPIService {
// ...
}
Update from Philip: Please don’t paste large code snippets here – please read my comments carefully.
Hi Ravi,
I’m happy to help – could you please either create a GitHub issue or a Stack Overflow question and send me the link as a reply to this comment? Please also add your existing test setup and further information about your project (e.g. dependency version, etc.).
The functionality of my blog’s comment section is quite limited to properly paste large code examples and seamlessly interact.
Kind regards,
Philip
https://stackoverflow.com/questions/67174216/how-to-test-given-class-having-static-method-using-mockito
Thanks, Ravi – let’s continue the discussion on Stack Overflow.
I have added my test case also
https://stackoverflow.com/q/68002163/15365716
Above the link of stack overflow question related to test
can you help I attach two screenshot . one is business class and one is its test class.
Hi Ravi,
sure – I’m happy to help. Let’s continue the discussion on Stack Overflow.
Kind regards,
Philip
Note from Philip: removed large code block
Hi Ravi,
please paste the code example on Stack Overflow and not here as a comment. The code formatting functionality of my blog’s commenting system is limited 🙁
Kind regards,
Philip
Hi,
Just to share that if like me, you decide to use version 3.4.0, you might get the error reported here https://github.com/mockito/mockito/issues/1967
Thanks for the hint Atem 🙂
With a Mockito version > 3.4.0 everything works for you?