Skip to content

Commit f00b917

Browse files
bmuschkotimtebeek
andauthored
Add PowerMockRunnerDelegateToRunWith recipe (#940)
* Add PowerMockRunnerDelegateToRunWith recipe Handles @PowerMockRunnerDelegate by promoting the delegate runner to @RunWith and removing the PowerMock-specific annotation and imports. * Regenerate recipes.csv with PowerMockRunnerDelegateToRunWith --------- Co-authored-by: Tim te Beek <tim@moderne.io>
1 parent fc88d78 commit f00b917

4 files changed

Lines changed: 242 additions & 16 deletions

File tree

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
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.Preconditions;
21+
import org.openrewrite.Recipe;
22+
import org.openrewrite.TreeVisitor;
23+
import org.openrewrite.internal.ListUtils;
24+
import org.openrewrite.java.AnnotationMatcher;
25+
import org.openrewrite.java.JavaIsoVisitor;
26+
import org.openrewrite.java.search.UsesType;
27+
import org.openrewrite.java.tree.Expression;
28+
import org.openrewrite.java.tree.J;
29+
30+
import java.util.Collections;
31+
import java.util.List;
32+
33+
public class PowerMockRunnerDelegateToRunWith extends Recipe {
34+
35+
private static final String POWER_MOCK_RUNNER_DELEGATE = "org.powermock.modules.junit4.PowerMockRunnerDelegate";
36+
private static final String POWER_MOCK_RUNNER = "org.powermock.modules.junit4.PowerMockRunner";
37+
private static final AnnotationMatcher DELEGATE_MATCHER =
38+
new AnnotationMatcher("@" + POWER_MOCK_RUNNER_DELEGATE);
39+
private static final AnnotationMatcher RUN_WITH_POWER_MOCK_RUNNER_MATCHER =
40+
new AnnotationMatcher("@org.junit.runner.RunWith(" + POWER_MOCK_RUNNER + ".class)");
41+
42+
@Getter
43+
final String displayName = "Replace `@PowerMockRunnerDelegate` with `@RunWith`";
44+
45+
@Getter
46+
final String description = "Replaces `@PowerMockRunnerDelegate(X.class)` by promoting the delegate runner " +
47+
"to `@RunWith(X.class)` and removing the PowerMock-specific annotation.";
48+
49+
@Override
50+
public TreeVisitor<?, ExecutionContext> getVisitor() {
51+
return Preconditions.check(
52+
new UsesType<>(POWER_MOCK_RUNNER_DELEGATE, false),
53+
new JavaIsoVisitor<ExecutionContext>() {
54+
@Override
55+
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
56+
J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
57+
58+
// Find @PowerMockRunnerDelegate and extract its argument
59+
Expression delegateRunnerArg = null;
60+
for (J.Annotation annotation : cd.getLeadingAnnotations()) {
61+
if (DELEGATE_MATCHER.matches(annotation)) {
62+
List<Expression> args = annotation.getArguments();
63+
if (args != null && !args.isEmpty()) {
64+
delegateRunnerArg = args.get(0);
65+
// Handle value = X.class form
66+
if (delegateRunnerArg instanceof J.Assignment) {
67+
delegateRunnerArg = ((J.Assignment) delegateRunnerArg).getAssignment();
68+
}
69+
}
70+
break;
71+
}
72+
}
73+
74+
if (delegateRunnerArg == null) {
75+
return cd;
76+
}
77+
78+
// Replace @RunWith(PowerMockRunner.class) argument with the delegate runner,
79+
// and remove @PowerMockRunnerDelegate
80+
final Expression finalDelegateRunnerArg = delegateRunnerArg;
81+
cd = cd.withLeadingAnnotations(ListUtils.map(cd.getLeadingAnnotations(), annotation -> {
82+
if (RUN_WITH_POWER_MOCK_RUNNER_MATCHER.matches(annotation)) {
83+
return annotation.withArguments(Collections.singletonList(finalDelegateRunnerArg));
84+
}
85+
if (DELEGATE_MATCHER.matches(annotation)) {
86+
return null;
87+
}
88+
return annotation;
89+
}));
90+
91+
maybeRemoveImport(POWER_MOCK_RUNNER_DELEGATE);
92+
maybeRemoveImport(POWER_MOCK_RUNNER);
93+
94+
return cd;
95+
}
96+
}
97+
);
98+
}
99+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ recipeList:
4444
- org.openrewrite.java.ChangeMethodTargetToStatic:
4545
methodPattern: org.powermock.api.mockito.PowerMockito when(..)
4646
fullyQualifiedTargetTypeName: org.mockito.Mockito
47+
- org.openrewrite.java.testing.mockito.PowerMockRunnerDelegateToRunWith
4748
- org.openrewrite.java.testing.mockito.PowerMockitoMockStaticToMockito
4849
- org.openrewrite.java.testing.mockito.PowerMockitoWhenNewToMockito
4950
- org.openrewrite.java.testing.mockito.CleanupPowerMockImports

0 commit comments

Comments
 (0)