Skip to content

Commit 43acae3

Browse files
authored
Refactor PowerMockito recipes for clarity and single responsibility (#943)
* Refactor PowerMockitoMockStaticToMockito for clarity and single responsibility Extract independent responsibilities from the monolithic 557-line recipe: - Extract `RemovePowerMockClassExtensions` recipe for removing `extends PowerMockConfiguration` and `extends PowerMockTestCase` - Fold `@RunWith(PowerMockRunner.class)` removal (with and without delegate) into `PowerMockRunnerDelegateToRunWith` - Replace 8 mutable instance fields with a `TestFramework` enum - Add early returns throughout to reduce nesting and improve readability * Clean up PowerMockitoMockStaticToMockito visitor - Consolidate isStaticMockAlreadyClosed/isStaticMockAlreadyOpened into single hasMatchingInvocation method parameterized by MethodMatcher - Restructure visitMethodInvocation with early returns for clarity - Fix double getTestGroupsAsString() call in maybeAddTearDownMethodBody - Simplify mockStatic tracking to use direct list access instead of Optional stream * Improve PowerMockRunnerDelegateToRunWith per review feedback - Use service(AnnotationService.class) instead of manual stream check - Extract findDelegateRunnerArg method to avoid finalDelegateRunnerArg - Move shared maybeRemoveImport(POWER_MOCK_RUNNER) above if/else so each branch can return immediately * Move import removal before withLeadingAnnotations for immediate returns * Update src/main/java/org/openrewrite/java/testing/mockito/PowerMockRunnerDelegateToRunWith.java
1 parent a72d7b1 commit 43acae3

9 files changed

Lines changed: 444 additions & 345 deletions

File tree

src/main/java/org/openrewrite/java/testing/mockito/PowerMockRunnerDelegateToRunWith.java

Lines changed: 47 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package org.openrewrite.java.testing.mockito;
1717

1818
import lombok.Getter;
19+
import org.jspecify.annotations.Nullable;
1920
import org.openrewrite.ExecutionContext;
2021
import org.openrewrite.Preconditions;
2122
import org.openrewrite.Recipe;
@@ -24,6 +25,7 @@
2425
import org.openrewrite.java.AnnotationMatcher;
2526
import org.openrewrite.java.JavaIsoVisitor;
2627
import org.openrewrite.java.search.UsesType;
28+
import org.openrewrite.java.service.AnnotationService;
2729
import org.openrewrite.java.tree.Expression;
2830
import org.openrewrite.java.tree.J;
2931

@@ -40,58 +42,72 @@ public class PowerMockRunnerDelegateToRunWith extends Recipe {
4042
new AnnotationMatcher("@org.junit.runner.RunWith(" + POWER_MOCK_RUNNER + ".class)");
4143

4244
@Getter
43-
final String displayName = "Replace `@PowerMockRunnerDelegate` with `@RunWith`";
45+
final String displayName = "Replace PowerMock runner with JUnit `@RunWith`";
4446

4547
@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+
final String description = "Replaces `@RunWith(PowerMockRunner.class)`. If `@PowerMockRunnerDelegate(X.class)` " +
49+
"is present, promotes the delegate runner to `@RunWith(X.class)`. Otherwise, removes the " +
50+
"`@RunWith(PowerMockRunner.class)` annotation entirely.";
4851

4952
@Override
5053
public TreeVisitor<?, ExecutionContext> getVisitor() {
5154
return Preconditions.check(
52-
new UsesType<>(POWER_MOCK_RUNNER_DELEGATE, false),
55+
Preconditions.or(
56+
new UsesType<>(POWER_MOCK_RUNNER_DELEGATE, false),
57+
new UsesType<>(POWER_MOCK_RUNNER, false)
58+
),
5359
new JavaIsoVisitor<ExecutionContext>() {
5460
@Override
5561
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
5662
J.ClassDeclaration cd = super.visitClassDeclaration(classDecl, ctx);
5763

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-
}
64+
if (!service(AnnotationService.class).matches(getCursor(), RUN_WITH_POWER_MOCK_RUNNER_MATCHER)) {
65+
return cd;
7266
}
7367

74-
if (delegateRunnerArg == null) {
75-
return cd;
68+
maybeRemoveImport(POWER_MOCK_RUNNER);
69+
70+
Expression delegateRunnerArg = findDelegateRunnerArg(cd);
71+
if (delegateRunnerArg != null) {
72+
// Replace @RunWith(PowerMockRunner.class) argument with the delegate runner,
73+
// and remove @PowerMockRunnerDelegate
74+
maybeRemoveImport(POWER_MOCK_RUNNER_DELEGATE);
75+
return cd.withLeadingAnnotations(ListUtils.map(cd.getLeadingAnnotations(), annotation -> {
76+
if (RUN_WITH_POWER_MOCK_RUNNER_MATCHER.matches(annotation)) {
77+
return annotation.withArguments(Collections.singletonList(delegateRunnerArg));
78+
}
79+
if (DELEGATE_MATCHER.matches(annotation)) {
80+
return null;
81+
}
82+
return annotation;
83+
}));
7684
}
7785

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 -> {
86+
// No delegate — just remove @RunWith(PowerMockRunner.class)
87+
maybeRemoveImport("org.junit.runner.RunWith");
88+
return cd.withLeadingAnnotations(ListUtils.map(cd.getLeadingAnnotations(), annotation -> {
8289
if (RUN_WITH_POWER_MOCK_RUNNER_MATCHER.matches(annotation)) {
83-
return annotation.withArguments(Collections.singletonList(finalDelegateRunnerArg));
84-
}
85-
if (DELEGATE_MATCHER.matches(annotation)) {
8690
return null;
8791
}
8892
return annotation;
8993
}));
94+
}
9095

91-
maybeRemoveImport(POWER_MOCK_RUNNER_DELEGATE);
92-
maybeRemoveImport(POWER_MOCK_RUNNER);
93-
94-
return cd;
96+
private @Nullable Expression findDelegateRunnerArg(J.ClassDeclaration cd) {
97+
for (J.Annotation annotation : cd.getLeadingAnnotations()) {
98+
if (DELEGATE_MATCHER.matches(annotation)) {
99+
List<Expression> args = annotation.getArguments();
100+
if (args != null && !args.isEmpty()) {
101+
Expression arg = args.get(0);
102+
if (arg instanceof J.Assignment) {
103+
return ((J.Assignment) arg).getAssignment();
104+
}
105+
return arg;
106+
}
107+
return null;
108+
}
109+
}
110+
return null;
95111
}
96112
}
97113
);

0 commit comments

Comments
 (0)