Skip to content

Commit f1b202d

Browse files
authored
Use mockito-inline instead of mockito-core where applicable (#923)
* ReplacePowerMockito: use mockito-inline instead of mockito-core (#1934) PowerMock features (static mocking, constructor mocking, final class mocking) require the inline mock maker, which is only bundled in mockito-inline for Mockito 3.x/4.x. Using mockito-core alone causes runtime failures when the migrated code calls Mockito.mockStatic(). * ReplacePowerMockito: conditionally select mockito-core vs mockito-inline (#1934) Replace hardcoded mockito-inline dependency with a ScanningRecipe that detects mockStatic(), whenNew(), or @PrepareForTest usage and selects mockito-inline only when needed, falling back to mockito-core otherwise. * Add ReplacePowerMockDependencies to recipes.csv (#1934) * ReplacePowerMockDependencies: multi-module project support (#1934) Use Map<JavaProject, Boolean> in the accumulator so each sub-module gets mockito-inline or mockito-core based on its own usage patterns.
1 parent 63820f4 commit f1b202d

4 files changed

Lines changed: 723 additions & 29 deletions

File tree

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Copyright 2026 the original author or authors.
3+
* <p>
4+
* Licensed under the Moderne Source Available License (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p>
8+
* https://docs.moderne.io/licensing/moderne-source-available-license
9+
* <p>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.openrewrite.java.testing.mockito;
17+
18+
import lombok.Getter;
19+
import org.openrewrite.ExecutionContext;
20+
import org.openrewrite.ScanningRecipe;
21+
import org.openrewrite.Tree;
22+
import org.openrewrite.TreeVisitor;
23+
import org.openrewrite.java.MethodMatcher;
24+
import org.openrewrite.java.dependencies.ChangeDependency;
25+
import org.openrewrite.java.marker.JavaProject;
26+
import org.openrewrite.java.tree.JavaSourceFile;
27+
import org.openrewrite.java.tree.JavaType;
28+
29+
import java.util.HashMap;
30+
import java.util.Map;
31+
32+
public class ReplacePowerMockDependencies extends ScanningRecipe<ReplacePowerMockDependencies.Accumulator> {
33+
34+
@Getter
35+
final String displayName = "Replace PowerMock dependencies with Mockito equivalents";
36+
37+
@Getter
38+
final String description = "Replaces PowerMock API dependencies with `mockito-inline` when `mockStatic()`, " +
39+
"`whenNew()`, or `@PrepareForTest` usage is detected, or `mockito-core` otherwise. PowerMock features " +
40+
"like static mocking, constructor mocking, and final class mocking require the inline mock maker " +
41+
"which is bundled in `mockito-inline` for Mockito 3.x/4.x.";
42+
43+
static class Accumulator {
44+
Map<JavaProject, Boolean> needsInlineMocking = new HashMap<>();
45+
}
46+
47+
@Override
48+
public Accumulator getInitialValue(ExecutionContext ctx) {
49+
return new Accumulator();
50+
}
51+
52+
private static final String PREPARE_FOR_TEST = "org.powermock.core.classloader.annotations.PrepareForTest";
53+
54+
@Override
55+
public TreeVisitor<?, ExecutionContext> getScanner(Accumulator acc) {
56+
MethodMatcher mockStaticMatcher = new MethodMatcher("org.powermock.api.mockito.PowerMockito mockStatic(..)");
57+
MethodMatcher whenNewMatcher = new MethodMatcher("org.powermock.api.mockito.PowerMockito whenNew(..)");
58+
return new TreeVisitor<Tree, ExecutionContext>() {
59+
@Override
60+
public Tree preVisit(Tree tree, ExecutionContext ctx) {
61+
stopAfterPreVisit();
62+
if (tree instanceof JavaSourceFile) {
63+
JavaProject project = tree.getMarkers().findFirst(JavaProject.class).orElse(null);
64+
if (Boolean.TRUE.equals(acc.needsInlineMocking.get(project))) {
65+
return tree;
66+
}
67+
JavaSourceFile sourceFile = (JavaSourceFile) tree;
68+
for (JavaType.Method type : sourceFile.getTypesInUse().getUsedMethods()) {
69+
if (mockStaticMatcher.matches(type) || whenNewMatcher.matches(type)) {
70+
acc.needsInlineMocking.put(project, true);
71+
return tree;
72+
}
73+
}
74+
for (JavaType type : sourceFile.getTypesInUse().getTypesInUse()) {
75+
if (type instanceof JavaType.FullyQualified &&
76+
PREPARE_FOR_TEST.equals(((JavaType.FullyQualified) type).getFullyQualifiedName())) {
77+
acc.needsInlineMocking.put(project, true);
78+
return tree;
79+
}
80+
}
81+
}
82+
return tree;
83+
}
84+
};
85+
}
86+
87+
@Override
88+
public TreeVisitor<?, ExecutionContext> getVisitor(Accumulator acc) {
89+
return new TreeVisitor<Tree, ExecutionContext>() {
90+
@Override
91+
public Tree preVisit(Tree tree, ExecutionContext ctx) {
92+
stopAfterPreVisit();
93+
JavaProject project = tree.getMarkers().findFirst(JavaProject.class).orElse(null);
94+
String targetArtifact = Boolean.TRUE.equals(acc.needsInlineMocking.get(project))
95+
? "mockito-inline" : "mockito-core";
96+
doAfterVisit(new ChangeDependency(
97+
"org.powermock", "powermock-api-mockito",
98+
"org.mockito", targetArtifact, "3.x",
99+
null, null, null).getVisitor());
100+
doAfterVisit(new ChangeDependency(
101+
"org.powermock", "powermock-api-mockito2",
102+
"org.mockito", targetArtifact, "3.x",
103+
null, null, null).getVisitor());
104+
return tree;
105+
}
106+
};
107+
}
108+
}

src/main/resources/META-INF/rewrite/powermockito.yml

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ tags:
2525
- testing
2626
- mockito
2727
recipeList:
28+
- org.openrewrite.java.testing.mockito.ReplacePowerMockDependencies
2829
- org.openrewrite.java.RemoveAnnotation:
2930
annotationPattern: "@org.powermock.core.classloader.annotations.PowerMockIgnore"
3031
- org.openrewrite.java.ChangeMethodTargetToStatic:
@@ -46,20 +47,6 @@ recipeList:
4647
- org.openrewrite.java.testing.mockito.PowerMockitoMockStaticToMockito
4748
- org.openrewrite.java.testing.mockito.PowerMockitoWhenNewToMockito
4849
- org.openrewrite.java.testing.mockito.CleanupPowerMockImports
49-
# powermock-api-mockito (Mockito 1.x bridge) and powermock-api-mockito2 (Mockito 2.x bridge)
50-
# are mutually exclusive; a project will only ever have one of them.
51-
- org.openrewrite.java.dependencies.ChangeDependency:
52-
oldGroupId: org.powermock
53-
oldArtifactId: powermock-api-mockito
54-
newGroupId: org.mockito
55-
newArtifactId: mockito-core
56-
newVersion: 3.x
57-
- org.openrewrite.java.dependencies.ChangeDependency:
58-
oldGroupId: org.powermock
59-
oldArtifactId: powermock-api-mockito2
60-
newGroupId: org.mockito
61-
newArtifactId: mockito-core
62-
newVersion: 3.x
6350
- org.openrewrite.java.dependencies.RemoveDependency:
6451
groupId: org.powermock
6552
artifactId: powermock*

0 commit comments

Comments
 (0)