Skip to content

Commit 0eb4765

Browse files
authored
Skip JUnit 4 lifecycle annotation when Jupiter equivalent already present (#1043)
When a method carries both a JUnit 4 lifecycle annotation (e.g. @before) and its Jupiter equivalent (@beforeeach), changing the type of the JUnit 4 annotation produced a duplicate, non-repeatable annotation. Remove the JUnit 4 annotation instead in that case. Fixes #501
1 parent e471570 commit 0eb4765

2 files changed

Lines changed: 79 additions & 4 deletions

File tree

src/main/java/org/openrewrite/java/testing/junit5/UpdateBeforeAfterAnnotations.java

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,30 @@
2020
import org.openrewrite.Preconditions;
2121
import org.openrewrite.Recipe;
2222
import org.openrewrite.TreeVisitor;
23+
import org.openrewrite.java.AnnotationMatcher;
2324
import org.openrewrite.java.ChangeType;
2425
import org.openrewrite.java.JavaIsoVisitor;
26+
import org.openrewrite.java.RemoveAnnotationVisitor;
2527
import org.openrewrite.java.search.UsesType;
2628
import org.openrewrite.java.tree.J;
2729

30+
import java.util.LinkedHashMap;
31+
import java.util.Map;
32+
2833
public class UpdateBeforeAfterAnnotations extends Recipe {
2934
@Getter
3035
final String displayName = "Migrate JUnit 4 lifecycle annotations to JUnit Jupiter";
3136

3237
@Getter
3338
final String description = "Replace JUnit 4's `@Before`, `@BeforeClass`, `@After`, and `@AfterClass` annotations with their JUnit Jupiter equivalents.";
3439

40+
private static final Map<String, String> JUNIT4_TO_JUPITER = new LinkedHashMap<String, String>() {{
41+
put("org.junit.Before", "org.junit.jupiter.api.BeforeEach");
42+
put("org.junit.After", "org.junit.jupiter.api.AfterEach");
43+
put("org.junit.BeforeClass", "org.junit.jupiter.api.BeforeAll");
44+
put("org.junit.AfterClass", "org.junit.jupiter.api.AfterAll");
45+
}};
46+
3547
@Override
3648
public TreeVisitor<?, ExecutionContext> getVisitor() {
3749
return Preconditions.check(Preconditions.or(
@@ -48,11 +60,30 @@ public static class UpdateBeforeAfterAnnotationsVisitor extends JavaIsoVisitor<E
4860
@Override
4961
public J preVisit(J tree, ExecutionContext ctx) {
5062
stopAfterPreVisit();
51-
doAfterVisit(new ChangeType("org.junit.Before", "org.junit.jupiter.api.BeforeEach", true).getVisitor());
52-
doAfterVisit(new ChangeType("org.junit.After", "org.junit.jupiter.api.AfterEach", true).getVisitor());
53-
doAfterVisit(new ChangeType("org.junit.BeforeClass", "org.junit.jupiter.api.BeforeAll", true).getVisitor());
54-
doAfterVisit(new ChangeType("org.junit.AfterClass", "org.junit.jupiter.api.AfterAll", true).getVisitor());
63+
// Remove the JUnit 4 annotation where the Jupiter equivalent is already present, to avoid creating a
64+
// duplicate annotation when the type is subsequently changed below.
65+
doAfterVisit(new RemoveRedundantJUnit4LifecycleAnnotations());
66+
JUNIT4_TO_JUPITER.forEach((from, to) -> doAfterVisit(new ChangeType(from, to, true).getVisitor()));
5567
return tree;
5668
}
5769
}
70+
71+
private static class RemoveRedundantJUnit4LifecycleAnnotations extends JavaIsoVisitor<ExecutionContext> {
72+
73+
@Override
74+
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
75+
J.MethodDeclaration md = super.visitMethodDeclaration(method, ctx);
76+
for (Map.Entry<String, String> pair : JUNIT4_TO_JUPITER.entrySet()) {
77+
AnnotationMatcher junit4 = new AnnotationMatcher("@" + pair.getKey());
78+
AnnotationMatcher jupiter = new AnnotationMatcher("@" + pair.getValue());
79+
boolean hasJunit4 = md.getLeadingAnnotations().stream().anyMatch(junit4::matches);
80+
boolean hasJupiter = md.getLeadingAnnotations().stream().anyMatch(jupiter::matches);
81+
if (hasJunit4 && hasJupiter) {
82+
md = (J.MethodDeclaration) new RemoveAnnotationVisitor(junit4)
83+
.visitNonNull(md, ctx, getCursor().getParentOrThrow());
84+
}
85+
}
86+
return md;
87+
}
88+
}
5889
}

src/test/java/org/openrewrite/java/testing/junit5/UpdateBeforeAfterAnnotationsTest.java

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,50 @@ void before() {
209209
);
210210
}
211211

212+
@Issue("https://github.com/openrewrite/rewrite-testing-frameworks/issues/501")
213+
@Test
214+
void removeJUnit4AnnotationWhenJupiterEquivalentAlreadyPresent() {
215+
//language=java
216+
rewriteRun(
217+
spec -> spec.parser(JavaParser.fromJavaVersion()
218+
.classpathFromResources(new InMemoryExecutionContext(), "junit-4", "junit-jupiter-api")),
219+
java(
220+
"""
221+
import org.junit.Before;
222+
import org.junit.After;
223+
import org.junit.jupiter.api.BeforeEach;
224+
import org.junit.jupiter.api.AfterEach;
225+
226+
class TestBase {
227+
@Before
228+
@BeforeEach
229+
void setup() {
230+
}
231+
232+
@After
233+
@AfterEach
234+
void tearDown() {
235+
}
236+
}
237+
""",
238+
"""
239+
import org.junit.jupiter.api.BeforeEach;
240+
import org.junit.jupiter.api.AfterEach;
241+
242+
class TestBase {
243+
@BeforeEach
244+
void setup() {
245+
}
246+
247+
@AfterEach
248+
void tearDown() {
249+
}
250+
}
251+
"""
252+
)
253+
);
254+
}
255+
212256
@Issue("https://github.com/openrewrite/rewrite-testing-frameworks/issues/59")
213257
@Test
214258
void retainPublicModifierOnOverriddenMethod() {

0 commit comments

Comments
 (0)