Skip to content

Commit 6a9f229

Browse files
magicwerkThomas Mauchtimtebeek
authored
AddMissingTestBeforeAfterAnnotations recipe for overridden JUnit test methods (#444)
* fix-issue-441 * fix issue 443 * Add precondition & push logic into LifecyleAnnotation * Remove trailing whitespace and excess newlines --------- Co-authored-by: Thomas Mauch <[email protected]> Co-authored-by: Tim te Beek <[email protected]>
1 parent e7f0148 commit 6a9f229

File tree

4 files changed

+295
-0
lines changed

4 files changed

+295
-0
lines changed
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
/*
2+
* Copyright 2023 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (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://www.apache.org/licenses/LICENSE-2.0
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.junit5;
17+
18+
import lombok.EqualsAndHashCode;
19+
import lombok.Value;
20+
import org.openrewrite.ExecutionContext;
21+
import org.openrewrite.Preconditions;
22+
import org.openrewrite.Recipe;
23+
import org.openrewrite.TreeVisitor;
24+
import org.openrewrite.java.AnnotationMatcher;
25+
import org.openrewrite.java.JavaIsoVisitor;
26+
import org.openrewrite.java.JavaParser;
27+
import org.openrewrite.java.JavaTemplate;
28+
import org.openrewrite.java.tree.J;
29+
import org.openrewrite.java.tree.JavaType.FullyQualified;
30+
import org.openrewrite.java.tree.JavaType.Method;
31+
import org.openrewrite.java.tree.TypeUtils;
32+
import org.openrewrite.marker.SearchResult;
33+
34+
import java.util.Comparator;
35+
import java.util.Optional;
36+
import java.util.function.Predicate;
37+
38+
@Value
39+
@EqualsAndHashCode(callSuper = false)
40+
public class AddMissingTestBeforeAfterAnnotations extends Recipe {
41+
@Override
42+
public String getDisplayName() {
43+
return "Add missing `@BeforeEach`, `@AfterEach`, `@Test` to overriding methods";
44+
}
45+
46+
@Override
47+
public String getDescription() {
48+
return "Adds `@BeforeEach`, `@AfterEach`, `@Test` to methods overriding superclass methods if the annoations are present on the superclass method.";
49+
}
50+
51+
@Override
52+
public TreeVisitor<?, ExecutionContext> getVisitor() {
53+
return Preconditions.check(new JavaIsoVisitor<ExecutionContext>() {
54+
@Override
55+
public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
56+
if (classDecl.getExtends() != null) {
57+
// Only classes that extend other classes can have override methods with missing annotations
58+
return SearchResult.found(classDecl);
59+
}
60+
return super.visitClassDeclaration(classDecl, ctx);
61+
}
62+
}, new AddMissingTestBeforeAfterAnnotationsVisitor());
63+
}
64+
65+
private static class AddMissingTestBeforeAfterAnnotationsVisitor extends JavaIsoVisitor<ExecutionContext> {
66+
@Override
67+
public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, ExecutionContext ctx) {
68+
if (!method.hasModifier(J.Modifier.Type.Static) && !method.isConstructor()) {
69+
Optional<Method> superMethod = TypeUtils.findOverriddenMethod(method.getMethodType());
70+
if (superMethod.isPresent()) {
71+
method = maybeAddMissingAnnotation(method, superMethod.get(), LifecyleAnnotation.BEFORE_EACH, ctx);
72+
method = maybeAddMissingAnnotation(method, superMethod.get(), LifecyleAnnotation.AFTER_EACH, ctx);
73+
method = maybeAddMissingAnnotation(method, superMethod.get(), LifecyleAnnotation.TEST, ctx);
74+
}
75+
}
76+
return super.visitMethodDeclaration(method, ctx);
77+
}
78+
79+
private J.MethodDeclaration maybeAddMissingAnnotation(J.MethodDeclaration method, Method superMethod, LifecyleAnnotation la, ExecutionContext ctx) {
80+
if (la.hasOldAnnotation(superMethod) && !la.hasNewAnnotation(method)) {
81+
maybeAddImport(la.annotation);
82+
return JavaTemplate.builder(la.simpleAnnotation)
83+
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "junit-jupiter-api-5.9"))
84+
.imports(la.annotation)
85+
.build()
86+
.apply(getCursor(), method.getCoordinates().addAnnotation(Comparator.comparing(J.Annotation::getSimpleName)));
87+
}
88+
return method;
89+
}
90+
}
91+
92+
enum LifecyleAnnotation {
93+
BEFORE_EACH("org.junit.jupiter.api.BeforeEach", "org.junit.Before"),
94+
AFTER_EACH("org.junit.jupiter.api.AfterEach", "org.junit.After"),
95+
TEST("org.junit.jupiter.api.Test", "org.junit.Test");
96+
97+
String annotation;
98+
String simpleAnnotation;
99+
private Predicate<FullyQualified> oldAnnotationPredicate;
100+
private AnnotationMatcher annotationMatcher;
101+
102+
LifecyleAnnotation(String annotation, String oldAnnotation) {
103+
this.annotation = annotation;
104+
this.simpleAnnotation = "@" + annotation.substring(annotation.lastIndexOf(".") + 1);
105+
this.oldAnnotationPredicate = n -> TypeUtils.isOfClassType(n, oldAnnotation);
106+
this.annotationMatcher = new AnnotationMatcher("@" + annotation);
107+
}
108+
109+
boolean hasOldAnnotation(Method method) {
110+
return method.getAnnotations().stream().anyMatch(oldAnnotationPredicate);
111+
}
112+
113+
boolean hasNewAnnotation(J.MethodDeclaration method) {
114+
return method.getAllAnnotations().stream().anyMatch(annotationMatcher::matches);
115+
}
116+
}
117+
}

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ recipeList:
7979
- org.openrewrite.java.testing.junit5.UpdateBeforeAfterAnnotations
8080
- org.openrewrite.java.testing.junit5.LifecycleNonPrivate
8181
- org.openrewrite.java.testing.junit5.UpdateTestAnnotation
82+
- org.openrewrite.java.testing.junit5.AddMissingTestBeforeAfterAnnotations
8283
- org.openrewrite.java.testing.junit5.ParameterizedRunnerToParameterized
8384
- org.openrewrite.java.testing.junit5.JUnitParamsRunnerToParameterized
8485
- org.openrewrite.java.testing.junit5.ExpectedExceptionToAssertThrows
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
* <p>
4+
* Licensed under the Apache License, Version 2.0 (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://www.apache.org/licenses/LICENSE-2.0
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.junit5;
17+
18+
import org.junit.jupiter.api.Test;
19+
import org.openrewrite.InMemoryExecutionContext;
20+
import org.openrewrite.java.JavaParser;
21+
import org.openrewrite.test.RecipeSpec;
22+
import org.openrewrite.test.RewriteTest;
23+
24+
import static org.openrewrite.java.Assertions.java;
25+
26+
class AddMissingTestBeforeAfterAnnotationsTest implements RewriteTest {
27+
@Override
28+
public void defaults(RecipeSpec spec) {
29+
spec
30+
.parser(JavaParser.fromJavaVersion()
31+
.classpathFromResources(new InMemoryExecutionContext(), "junit-4.13", "junit-jupiter-api-5.9"))
32+
.recipe(new AddMissingTestBeforeAfterAnnotations());
33+
}
34+
35+
@Test
36+
void addMissingTestBeforeAfterAnnotations() {
37+
//language=java
38+
rewriteRun(
39+
java(
40+
"""
41+
import org.junit.After;
42+
import org.junit.Before;
43+
import org.junit.Test;
44+
45+
public class AbstractTest {
46+
@Before
47+
public void before() {
48+
}
49+
50+
@After
51+
public void after() {
52+
}
53+
54+
@Test
55+
public void test() {
56+
}
57+
}
58+
"""
59+
),
60+
java(
61+
"""
62+
public class A extends AbstractTest {
63+
public void before() {
64+
}
65+
66+
public void after() {
67+
}
68+
69+
public void test() {
70+
}
71+
}
72+
""",
73+
"""
74+
import org.junit.jupiter.api.AfterEach;
75+
import org.junit.jupiter.api.BeforeEach;
76+
import org.junit.jupiter.api.Test;
77+
78+
public class A extends AbstractTest {
79+
@BeforeEach
80+
public void before() {
81+
}
82+
83+
@AfterEach
84+
public void after() {
85+
}
86+
87+
@Test
88+
public void test() {
89+
}
90+
}
91+
"""
92+
)
93+
);
94+
}
95+
}

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

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,4 +215,86 @@ void test() {
215215
)
216216
);
217217
}
218+
219+
@Issue("https://github.com/openrewrite/rewrite-testing-frameworks/issues/443")
220+
@Test
221+
void migrateInheritedTestBeforeAfterAnnotations() {
222+
//language=java
223+
rewriteRun(
224+
java(
225+
"""
226+
import org.junit.After;
227+
import org.junit.Before;
228+
import org.junit.Test;
229+
230+
public class AbstractTest {
231+
@Before
232+
public void before() {
233+
}
234+
235+
@After
236+
public void after() {
237+
}
238+
239+
@Test
240+
public void test() {
241+
}
242+
}
243+
""",
244+
"""
245+
import org.junit.jupiter.api.AfterEach;
246+
import org.junit.jupiter.api.BeforeEach;
247+
import org.junit.jupiter.api.Test;
248+
249+
public class AbstractTest {
250+
@BeforeEach
251+
public void before() {
252+
}
253+
254+
@AfterEach
255+
public void after() {
256+
}
257+
258+
@Test
259+
public void test() {
260+
}
261+
}
262+
"""
263+
),
264+
java(
265+
"""
266+
public class A extends AbstractTest {
267+
public void before() {
268+
}
269+
270+
public void after() {
271+
}
272+
273+
public void test() {
274+
}
275+
}
276+
""",
277+
"""
278+
import org.junit.jupiter.api.AfterEach;
279+
import org.junit.jupiter.api.BeforeEach;
280+
import org.junit.jupiter.api.Test;
281+
282+
public class A extends AbstractTest {
283+
@BeforeEach
284+
public void before() {
285+
}
286+
287+
@AfterEach
288+
public void after() {
289+
}
290+
291+
@Test
292+
public void test() {
293+
}
294+
}
295+
"""
296+
)
297+
);
298+
}
299+
218300
}

0 commit comments

Comments
 (0)