Static methods in Java have long been a thorn in the side of developers writing unit tests. Traditional mocking frameworks struggled with them because they couldn’t intercept calls to methods that belong to a class rather than an instance. For years, developers relied on powerful but heavyweight tools like PowerMock to test code with static dependencies.
That changed with Mockito 3.4.0. Today, Mockito offers native support for mocking static methods through its mockStatic() API, making it easier than ever to write clean, maintainable tests for code that depends on static calls.
In this guide, we’ll explore what static methods are, why they’re challenging to test, and how to use Mockito’s modern features to mock them effectively.
What are static methods in Java?
Static methods belong to a class rather than any particular instance of that class. You can call them directly on the class itself without creating an object.
For example, UUID.randomUUID() is a static method that you invoke on the UUID class to get the random UUID.
Static methods are commonly used in utility classes, logging systems, and configuration retrieval.
They’re convenient for operations that don’t require instance state, but they come with a significant drawback: they’re harder to test.
Why are static methods harder to test?
The fundamental challenge with static methods stems from how they’re bound at compile time. Unlike instance methods that can be overridden, static method calls are resolved directly to the class’s implementation. This creates tight coupling between your code and the static method’s behavior.
Traditional mocking works by creating substitute instances that you inject into your code under test. But with static methods, there’s no instance to substitute. The call goes straight to the implementation, making it difficult to isolate the code you’re testing from external dependencies.
When to mock static methods?
You’ll need to mock static methods when:
- The static method depends on the generated UUID (like UUID.randomUUID())
- The method interacts with external systems (databases, file systems, network calls)
- The method manages global state that would affect other tests
- You’re working with legacy code that heavily relies on static utilities
- The static call produces non-deterministic results
A classic example is testing business logic that depends on the generated UUID. Consider a service that generates a unique file name for an uploaded document:
Without mocking UUID.randomUUD(), you can't reliably test this method because it depends on the actual UUID.
public class FileNamingService {
public String generateUniqueFileName(String originalFileName) {
String uuid = UUID.randomUUID().toString();
String extension = "";
int lastDot = originalFileName.lastIndexOf('.');
if (lastDot > 0) {
extension = originalFileName.substring(lastDot);
}
return uuid + extension;
}
}
Can You Mock Static Methods in Mockito?
Yes, but this capability is relatively recent. For most of Mockito’s history, the answer was “no” unless you used external tools.
Before Mockito 3.4.0
Prior to version 3.4.0, Mockito couldn’t mock static methods at all. Developers had two options: refactor the code to avoid static calls, or use PowerMock to intercept them. PowerMock worked but required special test runners and added complexity to your build.
After Mockito 3.4.0
With the release of Mockito 3.4.0 in 2020, static method mocking became a native feature through Mockito.mockStatic(). This method creates a scoped mock for a class’s static methods without requiring special test runners or external frameworks.
Version requirements
- Mockito 3.4.0 – 4.x: You need the mockito-inline dependency to enable static mocking
- Mockito 5.0+: Static mocking works out of the box with mockito-core
Instance Mocks vs Static Mocks
The key difference between mocking instance methods and static methods lies in scope and lifecycle:
- Instance mocks exist as substitute objects throughout your test
- Static mocks are scoped to a specific code block and must be explicitly closed
- Instance mocks affect only the injected instance
- Static mocks affect all calls to the static method within their scope on the current thread
How to mock static methods using Mockito
You can mock a static method in four steps: 1) open a MockedStatic block, 2) stub the static method, 3) execute your test code, and 4) let the block close to restore the original behavior.
Let’s walk through mocking static methods with practical examples using our birthday service.
Setting Up Dependencies
//For Mockito 3.4.0 - 4.x, add the inline mock maker to your Maven dependencies:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-inline</artifactId>
<version>4.11.0</version>
<scope>test</scope>
</dependency>For Mockito 5.0+, the standard mockito-core dependency is sufficient.
Basic Static Method Mocking
Here’s how to test our FileNamingService by mocking UUID.randomUUID():
import java.util.UUID;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.mockStatic;
class FileNamingServiceTests {
private FileNamingService classUnderTest = new FileNamingService();
@Test
void shouldGenerateUniqueFileNameWithExtension() {
UUID fixedUuid = UUID.fromString("12345678-aaaa-bbbb-cccc-000000000000");
try (MockedStatic<UUID> mockedUuid = mockStatic(UUID.class)) {
mockedUuid.when(UUID::randomUUID).thenReturn(fixedUuid);
String fileName = classUnderTest.generateUniqueFileName("document.pdf");
assertEquals("12345678-aaaa-bbbb-cccc-000000000000.pdf", fileName);
}
}
@Test
void shouldGenerateUniqueFileNameWithoutExtension() {
UUID fixedUuid = UUID.fromString("12345678-aaaa-bbbb-cccc-000000000000");
try (MockedStatic<UUID> mockedUuid = mockStatic(UUID.class)) {
mockedUuid.when(UUID::randomUUID).thenReturn(fixedUuid);
String fileName = classUnderTest.generateUniqueFileName("document");
assertEquals("12345678-aaaa-bbbb-cccc-000000000000", fileName);
}
}
}
Key Points:
- Use try-with-resources to automatically close the static mock
- Use mockStatic(UUID.class)to instruct Mockito to mock this class
- Stub UUID::randomUUID with when(UUID::randomUUID).thenReturn(fixedUuid) to return the fixed UUID
- The service now uses your fixed UUID instead of random one
- Tests are deterministic and can run successfully on any day
Mockito mockStatic() explained
Understanding how mockStatic() works helps you use it effectively.
Under the hood
When you call Mockito.mockStatic(UUID.class), Mockito uses its inline mock maker to instrument the bytecode of that class. It essentially replaces static method implementations with stubs that Mockito controls. The result is a MockedStatic object that you use to configure and verify interactions.
Scope and lifecycle
The most critical aspect of MockedStatic is its scoped nature:
- Static mocks are thread-local, affecting only the thread that created them
- They remain active until explicitly closed via close() or until the try-with-resources block ends
- Once closed, static methods return to their original behavior
This design prevents static mocks from leaking between tests and ensures test isolation. In our file service example, UUID.randomUUID() returns a random UUID outside the try-with-resources block.
When to use
Use static mocking when:
- You’re testing code that depends on results of a static method call
- You’re testing legacy code with unavoidable static dependencies
- Refactoring to inject dependencies isn’t feasible
- The static method interacts with external systems you need to control in tests
When to avoid
Avoid static mocking when:
- You can refactor time-relevant code (e.g. LocalDate.now()) to use a Clock abstraction for time-dependent logic
- You’re testing new code where design improvements are possible
- The static method belongs to core Java classes that can be easily abstracted
For time-dependent code, consider injecting a Clock instance instead of calling LocalDate.now() directly:
public class BirthdayServiceAlternative {
private final Clock clock;
public BirthdayServiceAlternative(Clock clock) {
this.clock = clock;
}
public boolean isBirthday(LocalDate userBirthday) {
LocalDate today = LocalDate.now(clock);
MonthDay userBirthdayMonthDay = MonthDay.of(userBirthday.getMonth(), userBirthday.getDayOfMonth());
MonthDay todayMonthDay = MonthDay.from(today);
return userBirthdayMonthDay.equals(todayMonthDay);
}
}This approach is more testable and doesn’t require static mocking at all.
Missing the inline mock maker
The most common mistake is forgetting to include mockito-inline when using Mockito 3.x or 4.x. Without it, mockStatic() either throws an exception or silently fails. Always verify you have the correct dependency.
Not closing MockedStatic
Failing to close a static mock causes it to remain active, potentially affecting other tests:
// Bad: No try-with-resources
MockedStatic mock = mockStatic(UUID);
mock.when(UUID::randomUUID).thenReturn(fixedUuid);
// Mock never closed - will affect subsequent tests!
// Good: Automatic cleanup
try (MockedStatic mock = mockStatic(UUID.class)) {
mock.when(UUID::randomUUID).thenReturn(fixedUuid);
// Automatically closed at block end
}Thread safety considerations
Remember that static mocks are thread-local. If your code under test spawns new threads, those threads won’t see the mocked behavior. For most single-threaded unit tests, this isn’t an issue.
Overusing static mocks
Frequently needing to mock static methods often indicates a design problem. Consider refactoring to reduce coupling. For time-dependent logic, use Clock injection.
For utility methods, consider dependency injection. Static mocking should be a tool of last resort, not a primary testing strategy.
Best Practices:
- Always use try-with-resources for automatic cleanup
- Limit static mocking to unavoidable scenarios
- Focus on testing behavior, not implementation details
- Consider abstracting time-dependent code with Clock
- Avoid mocking core java library classes when alternatives exist
Conclusion
Mocking static methods in Java is no longer the headache it once was. With Mockito 3.4.0+, you can handle static dependencies cleanly and efficiently without heavyweight frameworks or complex configurations.
Remember these key takeaways:
- Use Mockito.mockStatic() with try-with-resources for automatic cleanup
- Ensure you have the right dependencies (mockito-inline for 3.4-4.x)
- Apply static mocking judiciously as a tool for unavoidable scenarios
- Consider refactoring over mocking when feasible (use Clock for time-dependent logic)
- Focus on testing behavior rather than implementation details
For teams looking to automate test generation while handling static methods intelligently, tools like Diffblue Cover can save significant time by automatically detecting and mocking static dependencies.
Whether you’re maintaining legacy code or building new applications, understanding how to mock static methods effectively will make your tests more robust and maintainable.
Try Diffblue Cover today!







