PowerMock With JUnit and Mockito: Examples and Alternatives

Learn PowerMock with JUnit 4 and Mockito for static, final, private and constructor mocks, why it fails on Java 17+, and how Mockito 5 replaces it.

PowerMock rewrites classes in a separate class loader under JUnit 4, while Mockito 5 uses an inline mock maker with scoped static mocks under JUnit 5

PowerMock is a mocking library that extends Mockito and EasyMock so that unit tests can mock static methods, constructors, final classes and private methods, which those frameworks once could not do. It runs each test class in its own class loader and rewrites the bytecode of the classes we name in @PrepareForTest.

Teams used PowerMock to test legacy code that calls static utilities or creates its dependencies with new. Today PowerMock is unmaintained, its last release 2.0.9 is from November 2020, it supports only JUnit 4 and Mockito 2 and 3, and on Java 17 and later it fails without extra JVM flags. Mockito 5 covers its main use cases.

The following example is a classic PowerMock test with JUnit 4 that replaces a static tax-rate lookup.

@RunWith(PowerMockRunner.class)
@PrepareForTest(TaxRates.class)
public class InvoiceServicePowerMockTest {

    @Test
    public void usesMockedTaxRate() {
        PowerMockito.mockStatic(TaxRates.class);
        Mockito.when(TaxRates.rateFor("DE")).thenReturn(0.19);

        double total = new InvoiceService().total("DE", 100.0);   // 119.0
        Assert.assertEquals(119.0, total, 0.001);
    }
}

Notice the JUnit 4 runner, which is the only way to activate PowerMock. In the next sections, we look at the project status, set up and use PowerMock for static, final, private and constructor mocking, run it on Java 25, and replace each case with Mockito 5.

1. Is PowerMock Still Maintained?

No. The PowerMock repository has had no release since 2.0.9 in November 2020, and the Maven Central metadata for powermock-api-mockito2 still lists 2.0.9 as the latest version. There is no JUnit 5 extension, and the Mockito module works with Mockito 2.x and 3.x but not with Mockito 4 or 5.

PowerMock rewrites classes in a separate class loader under JUnit 4, while Mockito 5 uses an inline mock maker with scoped static mocks under JUnit 5
PowerMock needs its own runner and class loader; Mockito 5 mocks statics and constructors in the normal test class loader
QuestionPowerMock 2.0.9Mockito 5.24.0
Last releaseNovember 2020September 2026
JUnit versionsJUnit 4 runner or rule onlyAny; mockito-junit-jupiter for JUnit 5 and 6
Static methodsPowerMockito.mockStatic()Mockito.mockStatic() since 3.4.0
ConstructorsPowerMockito.whenNew()Mockito.mockConstruction() since 3.5.0
Final classes and methodsYesYes, inline mock maker is the default since 5.0.0
Private methodsYesNo, by design
Java 17 and laterFails without –add-opensWorks; Java 21+ prints a warning unless Mockito is added as a Java agent

For new tests, we do not add PowerMock; existing PowerMock tests should move to Mockito 5 before the next Java upgrade. The rest of this article shows the PowerMock API, because many code bases still contain it, and the Mockito 5 code that replaces each part.

2. PowerMock Dependencies

PowerMock comes as an API module for the mocking framework and a module for the test framework. For Mockito, add powermock-api-mockito2 and powermock-module-junit4 with a compatible Mockito 3 version, because Mockito 4 and 5 removed internal APIs that PowerMock calls.

<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>
<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>3.12.4</version>
    <scope>test</scope>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.13.2</version>
    <scope>test</scope>
</dependency>

The powermock-api-mockito2 module brings powermock-reflect with it, which contains the Whitebox class for calling private methods. A project on JUnit 5 can still run these tests through the JUnit Vintage engine, but PowerMock features never work in JUnit Jupiter test classes.

3. The Invoice Service We Test

Our example is an invoice service with the four things that mocking frameworks used to struggle with. It calls a static method, has a final method, uses a private helper and creates a PdfRenderer with new.

class TaxRates {
    static double rateFor(String country) {
        return 0.20;                                  // in production, calls the tax service
    }
}
class PdfRenderer {
    byte[] render(String text) {
        return text.getBytes(StandardCharsets.UTF_8);
    }
}
class InvoiceService {
    double total(String country, double net) {
        return net + net * TaxRates.rateFor(country);
    }

    final String currency() {
        return "EUR";
    }

    int pdfSize(String text) {
        return new PdfRenderer().render(header() + text).length;
    }

    private String header() {
        return "INVOICE ";
    }
}
InvoiceService service = new InvoiceService();
double total = service.total("DE", 100.0);                   // 120.0
int size = service.pdfSize("total 120.0");                   // 19

In a unit test, we want total() to use a known rate without calling the tax service, and pdfSize() to work without a real renderer. Both dependencies are hard-wired, which is the situation PowerMock was built for.

4. Preparing the PowerMockito Runner

PowerMockito is the PowerMock API for Mockito. Two annotations on the test class activate it. The annotation @RunWith(PowerMockRunner.class) runs the class in a PowerMock class loader, and @PrepareForTest lists the classes whose bytecode PowerMock rewrites.

@RunWith(PowerMockRunner.class)
@PrepareForTest({TaxRates.class, InvoiceService.class})
public class InvoicePowerMockSetupTest {
}

The class to prepare depends on what we mock. For a static method or a final class, we prepare the class that declares it, such as TaxRates. For whenNew(), we prepare the class that calls new, here InvoiceService, not the class being created. The attribute fullyQualifiedNames accepts names with wildcards, for example @PrepareForTest(fullyQualifiedNames = “com.howtodoinjava.billing.*”) prepares every class in that package.

5. Mocking a Static Method With PowerMock

The call PowerMockito.mockStatic(TaxRates.class) replaces all static methods of the class with mock behavior, so unstubbed methods return default values such as 0.0 or null. We stub with the normal Mockito.when(), and we verify with PowerMockito.verifyStatic() followed by the static call to check.

@RunWith(PowerMockRunner.class)
@PrepareForTest(TaxRates.class)
public class TaxRatesPowerMockTest {

    @Test
    public void stubsAndVerifiesStaticCall() {
        PowerMockito.mockStatic(TaxRates.class);
        Mockito.when(TaxRates.rateFor("DE")).thenReturn(0.19);

        double total = new InvoiceService().total("DE", 100.0);  // 119.0
        Assert.assertEquals(119.0, total, 0.001);

        PowerMockito.verifyStatic(TaxRates.class, Mockito.times(1));
        TaxRates.rateFor("DE");
    }
}

Each verifyStatic() call checks one static call, the one that follows it. To keep the real behavior for some static methods, we use PowerMockito.spy(TaxRates.class) instead, which creates a partial static mock. A spy on an instance, such as spy(new TaxRates()), has no effect on static methods.

6. Mocking a Final Method

For final methods and final classes, PowerMockito.mock() creates a mock of the prepared class, and stubbing and verification use the normal Mockito methods.

@RunWith(PowerMockRunner.class)
@PrepareForTest(InvoiceService.class)
public class FinalMethodPowerMockTest {

    @Test
    public void stubsFinalMethod() {
        InvoiceService service = PowerMockito.mock(InvoiceService.class);
        Mockito.when(service.currency()).thenReturn("USD");

        Assert.assertEquals("USD", service.currency());
        Mockito.verify(service).currency();
    }
}

7. Mocking a Private Method

PowerMock stubs private methods on a spy, a partial mock that runs the real code for everything else. The method is named by a String, so a rename in the production code breaks the test only at runtime.

@RunWith(PowerMockRunner.class)
@PrepareForTest(InvoiceService.class)
public class PrivateMethodPowerMockTest {

    @Test
    public void stubsPrivateHeader() throws Exception {
        InvoiceService service = PowerMockito.spy(new InvoiceService());
        PowerMockito.doReturn("").when(service, "header");

        Assert.assertEquals(11, service.pdfSize("total 120.0"));
        PowerMockito.verifyPrivate(service, Mockito.times(1)).invoke("header");

        String realHeader = Whitebox.invokeMethod(new InvoiceService(), "header");
        Assert.assertEquals("INVOICE ", realHeader);
    }
}

The need to mock a private method is a sign that the class does two jobs. We either test the private code through the public method that calls it, or we move it to a separate class that we can mock in the normal way, which is also what we do when moving to Mockito.

8. Mocking Constructors With whenNew()

The method whenNew() makes every new PdfRenderer() inside the prepared class return a mock. That is the only way to replace a dependency that the class creates itself without changing its code.

@RunWith(PowerMockRunner.class)
@PrepareForTest(InvoiceService.class)
public class ConstructorPowerMockTest {

    @Test
    public void replacesRenderer() throws Exception {
        PdfRenderer renderer = Mockito.mock(PdfRenderer.class);
        Mockito.when(renderer.render(Mockito.anyString())).thenReturn(new byte[42]);
        PowerMockito.whenNew(PdfRenderer.class).withNoArguments().thenReturn(renderer);

        Assert.assertEquals(42, new InvoiceService().pdfSize("total 120.0"));
        PowerMockito.verifyNew(PdfRenderer.class).withNoArguments();
    }
}

9. Running PowerMock on Java 17 and Later

Since Java 16, and strictly since JEP 403 in Java 17, the JDK blocks reflective access to its internal classes. PowerMock 2.0.9 calls setAccessible() on methods of java.lang.Object, so a PowerMock test fails before or after the test method runs. On Java 25, with JUnit 4.13.2 and Mockito 3.12.4, the test from the intro fails with this output.

Tests run: 1,  Failures: 2
1) Test mechanism
java.lang.reflect.InaccessibleObjectException: Unable to make private final native void
java.lang.Object.wait0(long) throws java.lang.InterruptedException accessible:
module java.base does not "opens java.lang" to unnamed module @6e5e91e4
	at org.powermock.reflect.internal.WhiteboxImpl.doGetAllMethods(WhiteboxImpl.java:1508)

Opening the package to the unnamed module makes the same test pass, and the JVM also prints a warning that sun.misc.Unsafe::objectFieldOffset will be removed in a future release. With Maven Surefire, we pass the flag in argLine.

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

Tests that mock JDK classes or use other PowerMock features can need more –add-opens entries, such as java.base/java.util. Every new flag weakens the module checks for the whole test run, and once the JDK removes the sun.misc.Unsafe methods, the flags will no longer help.

10. Replacing PowerMock With Mockito 5

Mockito 5 uses the inline mock maker by default, which changes the bytecode of classes that are already loaded, so it needs no special runner or class loader to mock static methods and object construction. The examples use Mockito 5.24.0 and run in a plain JUnit 5 or JUnit 6 test.

Remove the PowerMock dependencies before the first Mockito 5 test runs. The powermock-api-mockito2 JAR registers its own PowerMockMaker through a mockito-extensions file, and while it is on the classpath, mockStatic() fails with “The used MockMaker PowerMockMaker does not support the creation of static mocks”.

10.1. Static Methods With mockStatic()

The method Mockito.mockStatic() returns a MockedStatic object, and the mock is active only for the current thread until we close it. Stubbing and verification take a lambda or a method reference with the static call. Our article on mocking static methods covers more variants.

InvoiceService service = new InvoiceService();
MockedStatic<TaxRates> taxRates = Mockito.mockStatic(TaxRates.class);
taxRates.when(() -> TaxRates.rateFor("DE")).thenReturn(0.19);

double mocked = service.total("DE", 100.0);                  // 119.0
taxRates.verify(() -> TaxRates.rateFor("DE"), Mockito.times(1));

taxRates.close();
double real = service.total("DE", 100.0);                    // 120.0

In a test, we open the mock in a try-with-resources block, so it is closed even when an assertion fails. A static mock that stays open leaks into other tests on the same thread, and the next mockStatic() call for that class fails with “static mocking is already registered in the current thread”.

InvoiceService service = new InvoiceService();
try (MockedStatic<TaxRates> taxRates = Mockito.mockStatic(TaxRates.class)) {
    taxRates.when(() -> TaxRates.rateFor(Mockito.anyString())).thenReturn(0.07);
    double reduced = service.total("DE", 100.0);             // 107.0
}

10.2. Constructors With mockConstruction()

The method Mockito.mockConstruction() turns every object created with new inside the scope into a mock. An optional initializer stubs each new mock, and constructed() returns the mocks in creation order.

InvoiceService service = new InvoiceService();
MockedConstruction<PdfRenderer> renderers = Mockito.mockConstruction(PdfRenderer.class,
        (mock, context) -> Mockito.when(mock.render(Mockito.anyString())).thenReturn(new byte[42]));

int mockedSize = service.pdfSize("total 120.0");             // 42
int created = renderers.constructed().size();                // 1

renderers.close();

Unlike whenNew(), the construction mock works no matter which class calls new, so there is nothing to prepare. Constructor injection is still the better design, because the test passes a mock and needs no bytecode tricks at all.

10.3. Final Classes and Methods

With the inline mock maker, Mockito.mock() handles final classes and final methods like any other. No configuration file and no mockito-inline artifact are needed in Mockito 5.

InvoiceService mock = Mockito.mock(InvoiceService.class);
Mockito.when(mock.currency()).thenReturn("USD");

String currency = mock.currency();                           // "USD"

10.4. Private Methods

Mockito does not mock private methods, and its authors consider that a design decision. A private method is part of the class under test, so we test it through the public method, or we extract it into its own class when it hides a dependency.

InvoiceService service = new InvoiceService();
int withHeader = service.pdfSize("");                        // 8, the header "INVOICE " alone

10.5. Java 21 and Later

Since Java 21, the JVM prints a warning when a library attaches a Java agent to the running JVM, as described in JEP 451, and a future JDK will block it by default. Mockito’s inline mock maker attaches itself in this way, so the Mockito documentation recommends adding mockito-core as a -javaagent in the test configuration.

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <argLine>@{argLine} -javaagent:@{org.mockito:mockito-core:jar}</argLine>
    </configuration>
</plugin>

The property org.mockito:mockito-core:jar holds the path of the Mockito JAR and is set by the properties goal of maven-dependency-plugin, which has to run before the tests. The @{…} form is Surefire’s late property replacement, which also keeps an argLine from other plugins such as JaCoCo. The Mockito Javadoc shows the Gradle setup with a separate mockitoAgent configuration.

11. PowerMock to Mockito 5 Migration Table

Moving a test class from PowerMock to Mockito is mostly mechanical. Remove the runner and @PrepareForTest, switch to JUnit 5 or 6 if needed, and translate each call.

PowerMock 2.0.9Mockito 5
@RunWith(PowerMockRunner.class)@ExtendWith(MockitoExtension.class) or nothing
@PrepareForTest(X.class)Not needed
PowerMockito.mockStatic(X.class) + Mockito.when(X.m())try (MockedStatic<X> s = mockStatic(X.class)) { s.when(X::m).thenReturn(v); }
PowerMockito.verifyStatic(X.class) + X.m()s.verify(X::m)
PowerMockito.whenNew(Y.class).withNoArguments().thenReturn(y)mockConstruction(Y.class, (mock, ctx) -> …)
PowerMockito.mock(FinalClass.class)Mockito.mock(FinalClass.class)
PowerMockito.doReturn(v).when(spy, “privateMethod”)Test through the public method or extract a class
Whitebox.invokeMethod(obj, “m”)Make the method package-private or test it through the public API
Whitebox.setInternalState(obj, “field”, v)Constructor or setter injection

A test class that uses Whitebox heavily tests implementation details. Rewriting it against the public behavior often removes the need for mocks of private state.

12. PowerMock FAQs

The JUnit version and the Java version are where PowerMock projects get stuck, so the answers start there.

12.1. Does PowerMock support JUnit 5?

No. PowerMock 2.0.9 ships a JUnit 4 runner and a JUnit 4 rule, and no JUnit Jupiter extension. A JUnit 5 project can run PowerMock tests only as JUnit 4 tests through the Vintage engine.

12.2. Can Mockito mock static methods without PowerMock?

Yes, since Mockito 3.4.0, with Mockito.mockStatic(). In Mockito 5, the required inline mock maker is the default, so it works with only mockito-core on the classpath.

12.3. Why does PowerMock fail with InaccessibleObjectException?

Java 17 and later deny reflective access to JDK internals, and PowerMock makes private methods of java.lang.Object accessible. Adding –add-opens java.base/java.lang=ALL-UNNAMED to the test JVM works around it, as in section 9, but migrating to Mockito 5 removes the problem.

12.4. Can PowerMock work with Mockito 4 or 5?

No. The module powermock-api-mockito2 depends on Mockito internals that changed in Mockito 4, so it works only with Mockito 2.x and 3.x. A project on Mockito 5 has to drop PowerMock.

12.5. How do we mock a private method with Mockito?

We cannot, and we should not need to. Test the private logic through the public method, or move it to a separate class and mock that class, which Mockito supports.

13. Conclusion

PowerMock made static, final, private and constructor mocking possible at a time when Mockito could not do any of them. It still works on JUnit 4 with Mockito 3, but it has had no release since 2020 and needs –add-opens flags on current Java versions.

Mockito 5 replaces the main features with mockStatic(), mockConstruction() and final mocking by default, all scoped and without a special runner. For private methods, the better fix is in the production code, through a public method or an extracted class.

14. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. @RunWith() Annotation is not resolving with powermock-2.0.9 , I tried with all the latest version’s of mockito such as 3.12.4 , 5.3.0 ,5.3.3 with this powermock version, but @RunWith is not resolving. Please provide some inputs for this issue to resolve.

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.