Skip to content

Commit beb6b19

Browse files
committed
Fix #5 Handle @RunWith(SpringRunner.class) in junit 4 -> 5 migration
This depends on currently unpublished changes in rewrite core libraries
1 parent e56ff66 commit beb6b19

File tree

3 files changed

+205
-0
lines changed

3 files changed

+205
-0
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ dependencies {
6868
testImplementation("org.junit.jupiter:junit-jupiter-api:latest.release")
6969
testImplementation("org.junit.jupiter:junit-jupiter-params:latest.release")
7070
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:latest.release")
71+
testRuntimeOnly("org.springframework:spring-test:latest.release")
7172
"afterImplementation"("org.junit.jupiter:junit-jupiter-api:latest.release")
7273
"afterImplementation"("org.junit.jupiter:junit-jupiter-params:latest.release")
7374
"afterRuntimeOnly"("org.junit.jupiter:junit-jupiter-engine:latest.release")
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
/*
2+
* Copyright 2020 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.openrewrite.Formatting;
19+
import org.openrewrite.java.JavaIsoRefactorVisitor;
20+
import org.openrewrite.java.search.SemanticallyEqual;
21+
import org.openrewrite.java.tree.J;
22+
import org.openrewrite.java.tree.JavaType;
23+
24+
import java.util.Collections;
25+
import java.util.List;
26+
import java.util.stream.Collectors;
27+
28+
import static org.openrewrite.Formatting.EMPTY;
29+
import static org.openrewrite.Tree.randomId;
30+
31+
/**
32+
* JUnit4 Spring test classes are annotated with @RunWith(SpringRunner.class)
33+
* Turn this into the JUnit5-compatible @ExtendsWith(SpringExtension.class)
34+
*/
35+
public class SpringRunnerToSpringExtension extends JavaIsoRefactorVisitor {
36+
private static final JavaType.Class runWithType = JavaType.Class.build("org.junit.runner.RunWith");
37+
private static final J.Ident runWithIdent = J.Ident.build(
38+
randomId(),
39+
"RunWith",
40+
runWithType,
41+
EMPTY);
42+
private static final JavaType.Class springRunnerType = JavaType.Class.build("org.springframework.test.context.junit4.SpringRunner");
43+
private static final JavaType.Class extendWithType = JavaType.Class.build("org.junit.jupiter.api.extension.ExtendWith");
44+
private static final J.Ident extendWithIdent = J.Ident.build(
45+
randomId(),
46+
"ExtendWith",
47+
extendWithType,
48+
EMPTY
49+
);
50+
private static final JavaType.Class springExtensionType = JavaType.Class.build("org.springframework.test.context.junit.jupiter.SpringExtension");
51+
52+
53+
// Reference @RunWith(SpringRunner.class) annotation for semantically equal to compare against
54+
private static final J.Annotation runWithSpringRunnerAnnotation = new J.Annotation(
55+
randomId(),
56+
runWithIdent,
57+
new J.Annotation.Arguments(
58+
randomId(),
59+
Collections.singletonList(
60+
new J.FieldAccess(
61+
randomId(),
62+
J.Ident.build(
63+
randomId(),
64+
"SpringRunner",
65+
springRunnerType,
66+
EMPTY
67+
),
68+
J.Ident.build(randomId(), "class", null, EMPTY),
69+
JavaType.Class.build("java.lang.Class"),
70+
EMPTY
71+
)
72+
),
73+
EMPTY
74+
),
75+
EMPTY
76+
);
77+
78+
private static final J.Annotation extendWithSpringExtensionAnnotation = new J.Annotation(
79+
randomId(),
80+
extendWithIdent,
81+
new J.Annotation.Arguments(
82+
randomId(),
83+
Collections.singletonList(
84+
new J.FieldAccess(
85+
randomId(),
86+
J.Ident.build(
87+
randomId(),
88+
"SpringExtension",
89+
springExtensionType,
90+
EMPTY
91+
),
92+
J.Ident.build(randomId(), "class", null, EMPTY),
93+
JavaType.Class.build("java.lang.Class"),
94+
EMPTY
95+
)
96+
),
97+
EMPTY
98+
),
99+
EMPTY
100+
);
101+
102+
public SpringRunnerToSpringExtension() {
103+
setCursoringOn();
104+
}
105+
106+
@Override
107+
public J.ClassDecl visitClassDecl(J.ClassDecl cd) {
108+
List<J.Annotation> annotations = cd.getAnnotations().stream()
109+
.map(this::springRunnerToSpringExtension)
110+
.collect(Collectors.toList());
111+
112+
return cd.withAnnotations(annotations);
113+
}
114+
115+
/**
116+
* Converts annotations like @RunWith(SpringRunner.class) into @ExtendWith(SpringExtension.class)
117+
* Leaves other annotations untouched and returns as-is.
118+
*
119+
* NOT a pure function. Side effects include:
120+
* Adding imports for ExtendWith and SpringExtension
121+
* Removing imports for RunWith and SpringRunner
122+
*/
123+
private J.Annotation springRunnerToSpringExtension(J.Annotation maybeSpringRunner) {
124+
if(!new SemanticallyEqual(runWithSpringRunnerAnnotation).visit(maybeSpringRunner)) {
125+
return maybeSpringRunner;
126+
}
127+
Formatting originalFormatting = maybeSpringRunner.getFormatting();
128+
J.ClassDecl parent = getCursor().firstEnclosing(J.ClassDecl.class);
129+
assert parent != null;
130+
J.Annotation extendWithSpringExtension = extendWithSpringExtensionAnnotation.withFormatting(originalFormatting);
131+
maybeAddImport(extendWithType);
132+
maybeAddImport(springExtensionType);
133+
maybeRemoveImport(springRunnerType);
134+
maybeRemoveImport(runWithType);
135+
136+
return extendWithSpringExtension;
137+
}
138+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2020 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.Test
19+
import org.openrewrite.RefactorVisitorTestForParser
20+
import org.openrewrite.java.JavaParser
21+
import org.openrewrite.java.tree.J
22+
23+
class SpringRunnerToSpringExtensionTest : RefactorVisitorTestForParser<J.CompilationUnit> {
24+
override val parser: JavaParser = JavaParser.fromJavaVersion()
25+
.classpath("junit", "spring-test")
26+
.build()
27+
28+
override val visitors = listOf(SpringRunnerToSpringExtension())
29+
30+
@Test
31+
fun basicRunnerToExtension() = assertRefactored(
32+
before = """
33+
import org.junit.runner.RunWith;
34+
import org.springframework.test.context.junit4.SpringRunner;
35+
36+
@RunWith(SpringRunner.class)
37+
class A {}
38+
""",
39+
after = """
40+
import org.junit.jupiter.api.extension.ExtendWith;
41+
import org.springframework.test.context.junit.jupiter.SpringExtension;
42+
43+
@ExtendWith(SpringExtension.class)
44+
class A {}
45+
"""
46+
)
47+
48+
@Test
49+
fun leavesOtherRunnersAlone() = assertUnchanged(
50+
before = """
51+
package a;
52+
53+
import org.junit.runner.RunWith;
54+
55+
@RunWith(B.class)
56+
class A {}
57+
""",
58+
dependencies = listOf(
59+
"""
60+
package a;
61+
62+
class B {}
63+
"""
64+
)
65+
)
66+
}

0 commit comments

Comments
 (0)