Mocking Static Methods in Java with PowerMock (and Mockito 3.4+)

Mocking Static Methods in Java with PowerMock (and Mockito 3.4+)

Static methods are the hardest thing to mock in Java. They're called directly on the class, not on an injected instance, so standard mocking frameworks — which work by creating proxy subclasses — can't intercept them. That's why two separate solutions exist: PowerMock's mockStatic(), which predates the problem being solvable any other way, and Mockito's MockedStatic API introduced in version 3.4.

This post covers both approaches in full: setup, syntax, verification, argument matching, and the real-world gotchas that will cost you hours if you don't know about them.


The Problem with Static Methods

Consider this code:

public class OrderService {
    public String createOrder(String userId, double amount) {
        String orderId = UUID.randomUUID().toString();
        AuditLog.record(orderId, userId, amount);
        return orderId;
    }
}

UUID.randomUUID() is static — you can't inject it. AuditLog.record() is static — same problem. Testing createOrder in isolation means either:

  1. Accepting non-deterministic behavior (real UUID, real audit log)
  2. Wrapping both in injectable adapters (correct design, real refactoring cost)
  3. Mocking the static calls (PowerMock or Mockito inline)

Option 3 is what this post covers.


PowerMock Approach

Setup

PowerMock requires specific dependencies and a custom JUnit runner. As of this writing, PowerMock 2.0.9 is the latest stable release and supports Mockito 3.x with JUnit 4.

Maven:

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-api-mockito2</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>

Gradle:

testImplementation 'org.powermock:powermock-module-junit4:2.0.9'
testImplementation 'org.powermock:powermock-api-mockito2:2.0.9'

@PrepareForTest

@PrepareForTest tells PowerMock which classes to rewrite before the test runs. This is mandatory — without it, mockStatic() will fail silently or throw an error.

The annotation accepts the class containing the static method you want to mock, not the class under test:

@RunWith(PowerMockRunner.class)
@PrepareForTest({UUID.class, AuditLog.class})
public class OrderServiceTest {
    // ...
}

You can prepare multiple classes. List every class whose static methods you intend to mock.

mockStatic()

@RunWith(PowerMockRunner.class)
@PrepareForTest({UUID.class, AuditLog.class})
public class OrderServiceTest {

    @Test
    public void shouldReturnMockedOrderId() {
        UUID mockUUID = UUID.fromString("00000000-0000-0000-0000-000000000001");
        PowerMockito.mockStatic(UUID.class);
        when(UUID.randomUUID()).thenReturn(mockUUID);

        PowerMockito.mockStatic(AuditLog.class);
        // void static — no return value to stub, just mock it

        OrderService service = new OrderService();
        String result = service.createOrder("user-42", 99.99);

        assertEquals("00000000-0000-0000-0000-000000000001", result);
    }
}

Key points:

  • PowerMockito.mockStatic(Clazz.class) activates mocking for that class's static methods within the current test
  • After mockStatic, when(StaticClass.method()) syntax works exactly like Mockito instance mocking
  • Void static methods are mocked automatically once mockStatic is called — no stub needed unless you want to verify or throw

verifyStatic()

Verifying static calls requires a two-step pattern that's different from Mockito's verify():

@Test
public void shouldRecordAuditLog() {
    PowerMockito.mockStatic(AuditLog.class);

    OrderService service = new OrderService();
    service.createOrder("user-42", 99.99);

    // Step 1: Tell PowerMock which class to verify
    PowerMockito.verifyStatic(AuditLog.class);
    // Step 2: Call the method you're verifying (this isn't a real call — it's captured)
    AuditLog.record(anyString(), eq("user-42"), eq(99.99));
}

This two-step pattern trips people up. The call on line "Step 2" looks like a real method invocation but isn't — it's captured by PowerMock for verification. If you forget step 1 and just call AuditLog.record(...), the verification won't happen.

Verifying call count:

PowerMockito.verifyStatic(AuditLog.class, times(2));
AuditLog.record(anyString(), anyString(), anyDouble());
PowerMockito.verifyStatic(AuditLog.class, never());
AuditLog.record(anyString(), anyString(), anyDouble());

Argument Matchers

Mockito argument matchers work with PowerMock static mocking:

PowerMockito.mockStatic(CacheManager.class);
when(CacheManager.get(eq("user:42"))).thenReturn(cachedUser);
when(CacheManager.get(startsWith("session:"))).thenReturn(null);
when(CacheManager.get(anyString())).thenReturn(null); // fallback

One rule: if you use a matcher for any argument, you must use matchers for all arguments. Mixing literal values and matchers in the same call will throw InvalidUseOfMatchersException:

// WRONG
when(CacheManager.get(eq("user:42"), 30)).thenReturn(cachedUser);

// RIGHT
when(CacheManager.get(eq("user:42"), eq(30))).thenReturn(cachedUser);

Stubbing Exceptions

PowerMockito.mockStatic(ExternalAPI.class);
when(ExternalAPI.fetch(anyString())).thenThrow(new NetworkException("timeout"));

assertThrows(NetworkException.class, () -> service.loadData("resource-1"));

Mockito 3.4+ Approach: MockedStatic

Starting with Mockito 3.4, you can mock static methods without PowerMock using MockedStatic. This requires the mockito-inline artifact (or Mockito 5.x, where it's built in).

Maven:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-inline</artifactId>
    <version>4.11.0</version>
    <scope>test</scope>
</dependency>

Gradle:

testImplementation 'org.mockito:mockito-inline:4.11.0'

MockedStatic Syntax

@Test
void shouldReturnMockedOrderId() {
    UUID fixedUuid = UUID.fromString("00000000-0000-0000-0000-000000000001");

    try (MockedStatic<UUID> mockedUUID = mockStatic(UUID.class)) {
        mockedUUID.when(UUID::randomUUID).thenReturn(fixedUuid);

        OrderService service = new OrderService();
        String result = service.createOrder("user-42", 99.99);

        assertEquals("00000000-0000-0000-0000-000000000001", result);
    }
    // Static mock is automatically cleaned up after try block
}

The try-with-resources pattern is important — MockedStatic implements AutoCloseable, and the static mock is only active within the try block. Outside it, the original static implementation is restored. Forgetting to close it leaks the mock into other tests.

Verification with MockedStatic

try (MockedStatic<AuditLog> mockedLog = mockStatic(AuditLog.class)) {
    service.createOrder("user-42", 99.99);

    mockedLog.verify(() -> AuditLog.record(anyString(), eq("user-42"), eq(99.99)));
    mockedLog.verify(() -> AuditLog.record(anyString(), anyString(), anyDouble()), times(1));
}

Much cleaner than PowerMock's two-step verifyStatic pattern. The lambda captures the call, matchers work as usual.

Verify never called:

mockedLog.verify(() -> AuditLog.record(anyString(), anyString(), anyDouble()), never());

Comparison Table

Feature PowerMock Mockito MockedStatic
JUnit version 4 (limited 5 support) 4 and 5
Mockito version Up to 3.x 3.4+
Java version 8–11 (16+ needs flags) 8+
Setup complexity High (@RunWith, @PrepareForTest) Low (try-with-resources)
Verification syntax Two-step verifyStatic Lambda in verify()
Argument matchers Yes Yes
Scope control Per-test-method (auto via runner) Explicit try block
Parallel test safety Poor (classloader isolation) Better
Active maintenance Dormant since ~2021 Actively maintained
Mockito 4/5 support No Yes

If you're starting a project today, use MockedStatic. If you're maintaining a JUnit 4 + Mockito 2/3 project, PowerMock may be what you already have.


Gotchas

ClassLoader Isolation in PowerMock

PowerMock rewrites bytecode for classes listed in @PrepareForTest. This happens in a separate classloader context. The consequence: if you're testing a class that has static initializers or class-level constants, those initializers re-run in the PowerMock classloader. In some cases this causes:

  • NullPointerException in static initializer blocks that assumed a real environment
  • ClassCastException when objects cross classloader boundaries (same class name, different loaders)
  • Spring beans or JPA entities failing to initialize

The fix for static initializer issues is to suppress them:

@SuppressStaticInitializationFor("com.example.ProblematicClass")

Use this sparingly — suppressing initialization can hide bugs.

@RunWith Conflicts

Only one @RunWith annotation is allowed per class. This creates conflicts when you need both PowerMock and another runner (e.g., Spring's SpringRunner, Parameterized, etc.):

// This doesn't work — only one @RunWith allowed
@RunWith(PowerMockRunner.class)
@RunWith(SpringRunner.class)
public class MyTest { ... }

Workaround using PowerMockRule (JUnit 4 only):

@RunWith(SpringRunner.class)
@PrepareForTest({StaticUtility.class})
public class MyTest {

    @Rule
    public PowerMockRule rule = new PowerMockRule();

    // Now you can use both Spring context and PowerMock
}

Requires the powermock-module-junit4-rule dependency:

<dependency>
    <groupId>org.powermock</groupId>
    <artifactId>powermock-module-junit4-rule</artifactId>
    <version>2.0.9</version>
    <scope>test</scope>
</dependency>

MockedStatic Scope Leaks

If you use mockStatic() without try-with-resources and forget to close it, the mock leaks to subsequent tests:

// WRONG — mock leaks
MockedStatic<UUID> leak = mockStatic(UUID.class);
leak.when(UUID::randomUUID).thenReturn(fixedUuid);
// test code...
// forgot: leak.close();
// RIGHT
try (MockedStatic<UUID> mockedUUID = mockStatic(UUID.class)) {
    mockedUUID.when(UUID::randomUUID).thenReturn(fixedUuid);
    // test code...
} // closed automatically

Leaked mocks cause org.mockito.exceptions.base.MockitoException: For com.example.Foo, static mocking is already registered in the current thread in subsequent tests.

Java Module System (Java 16+)

Both PowerMock and Mockito inline use reflection to instrument classes. Java 16+ enforces strong encapsulation by default, which can break both tools for JDK classes.

For Mockito inline on Java 16+:

-XX:+EnableDynamicAgentLoading

Or add to surefire plugin config in Maven:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <argLine>
            --add-opens java.base/java.lang=ALL-UNNAMED
            --add-opens java.base/java.util=ALL-UNNAMED
        </argLine>
    </configuration>
</plugin>

For Gradle, add to jvmArgs in the test block:

test {
    jvmArgs '--add-opens', 'java.base/java.lang=ALL-UNNAMED',
            '--add-opens', 'java.base/java.util=ALL-UNNAMED'
}

When Static Mocking Is a Design Smell

Needing to mock a static method is often a signal — not always, but often enough to ask the question.

You own the code and it's static: Consider whether the method needs to be static at all. A static UserValidator.validate(user) can become an injectable UserValidator service. One refactor, and you never need to mock it.

You call a static method in many places: The coupling is wide. A thin wrapper/adapter class (UUIDGenerator, Clock, SystemProperties) injected via constructor gives you mockability without framework magic.

You reach for static mocking in every test class: This is the strongest signal. If your test suite regularly needs mockStatic, the production code has a structural problem.

Static mocking of third-party code you don't own — UUID.randomUUID(), System.currentTimeMillis(), file system operations — is more defensible. Wrapping every JDK static call is excessive. Use MockedStatic for these cases and move on.


Unit tests with MockedStatic or PowerMock verify your logic in isolation. For validating the actual behavior users see — form submissions, API responses, multi-step flows — HelpMeTest lets you describe and run end-to-end tests in plain English, without writing a single line of test framework code.

Read more

Start now free