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.

| Question | PowerMock 2.0.9 | Mockito 5.24.0 |
|---|---|---|
| Last release | November 2020 | September 2026 |
| JUnit versions | JUnit 4 runner or rule only | Any; mockito-junit-jupiter for JUnit 5 and 6 |
| Static methods | PowerMockito.mockStatic() | Mockito.mockStatic() since 3.4.0 |
| Constructors | PowerMockito.whenNew() | Mockito.mockConstruction() since 3.5.0 |
| Final classes and methods | Yes | Yes, inline mock maker is the default since 5.0.0 |
| Private methods | Yes | No, by design |
| Java 17 and later | Fails without –add-opens | Works; 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.9 | Mockito 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
- PowerMock on GitHub
- PowerMock Mockito Usage
- Mockito Javadoc
- JEP 403, Strongly Encapsulate JDK Internals
- JEP 451, Prepare to Disallow the Dynamic Loading of Agents
Happy Learning !!
@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.
@RunWith is JUnit 4 annotation. In JUnit 5, you can either omit this annotation or use @ExtendWith.
Hi Lokesh,
Thank you for your response, I tried with Junit 4 only, @RunWith is not resolving in eclipse. PowerMock is not yet support Junit 5.
I have the sourcecode link in the post end. It has the pom.xml for dependencies and test class with all imports.