Skip to content

Commit 5abf325

Browse files
committed
Add ExpectedExceptionToAssertThrows visitor closes #9
1 parent ebd5381 commit 5abf325

File tree

7 files changed

+309
-66
lines changed

7 files changed

+309
-66
lines changed
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
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.java.tree.Expression;
19+
import org.openrewrite.java.tree.Flag;
20+
import org.openrewrite.java.tree.J;
21+
import org.openrewrite.java.tree.JavaType;
22+
import org.openrewrite.java.tree.Statement;
23+
24+
import java.util.Arrays;
25+
import java.util.Collections;
26+
import java.util.HashSet;
27+
import java.util.List;
28+
29+
import static org.openrewrite.Formatting.EMPTY;
30+
import static org.openrewrite.Formatting.format;
31+
import static org.openrewrite.Tree.randomId;
32+
33+
class AssertionsBuilder {
34+
/**
35+
* Produces a method invocation of Assertions.assertThrows(Class<T> expectedType, Executable executable)
36+
*
37+
* @param expectedTypeExpression an expression that is expected, but not verified, to evaluate to a Class<T extends Throwable>
38+
* @param Executable the list of statements used to create the body of the lambda that is expected to produce a particular type of expression
39+
*/
40+
public static J.MethodInvocation assertThrows(Expression expectedTypeExpression, List<Statement> Executable) {
41+
// The Java 11 Specification says that lambda bodies can be either a single expression or a block.
42+
// So put a block around anything that isn't exactly one expression.
43+
boolean isSingleExpression = Executable.size() == 1 && Executable.get(0) instanceof Expression;
44+
Statement assertBlock;
45+
if(isSingleExpression) {
46+
assertBlock = Executable.get(0).withPrefix(" ");
47+
} else {
48+
assertBlock = new J.Block<>(
49+
randomId(),
50+
null,
51+
Executable,
52+
format(" "),
53+
new J.Block.End(randomId(), format("\n"))
54+
);
55+
}
56+
57+
return new J.MethodInvocation(
58+
randomId(),
59+
null,
60+
null,
61+
J.Ident.build(randomId(), "assertThrows", JavaType.Primitive.Void, EMPTY),
62+
new J.MethodInvocation.Arguments(
63+
randomId(),
64+
Arrays.asList(
65+
expectedTypeExpression.withFormatting(EMPTY),
66+
new J.Lambda(
67+
randomId(),
68+
new J.Lambda.Parameters(
69+
randomId(),
70+
true,
71+
Collections.emptyList()
72+
),
73+
new J.Lambda.Arrow(randomId(), format(" ")),
74+
assertBlock,
75+
JavaType.Primitive.Void,
76+
format(" ")
77+
)
78+
),
79+
EMPTY
80+
),
81+
JavaType.Method.build(
82+
JavaType.Class.build("org.junit.jupiter.api.Assertions"),
83+
"assertThrows",
84+
null,
85+
new JavaType.Method.Signature(
86+
new JavaType.GenericTypeVariable("T", JavaType.Class.build("java.lang.Throwable")),
87+
Arrays.asList(JavaType.Class.build("java.lang.Class"), JavaType.Class.build("org.junit.jupiter.api.function.Executable"))),
88+
Arrays.asList("arg0", "arg1"),
89+
new HashSet<>(Arrays.asList(Flag.Public, Flag.Static))
90+
),
91+
format("\n")
92+
);
93+
}
94+
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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.java.AddImport;
19+
import org.openrewrite.java.AutoFormat;
20+
import org.openrewrite.java.JavaIsoRefactorVisitor;
21+
import org.openrewrite.java.tree.Expression;
22+
import org.openrewrite.java.tree.J;
23+
import org.openrewrite.java.tree.JavaType;
24+
import org.openrewrite.java.tree.Statement;
25+
import org.openrewrite.java.tree.TypeUtils;
26+
27+
import java.util.ArrayList;
28+
import java.util.List;
29+
30+
import static java.util.Collections.singletonList;
31+
32+
/**
33+
* Replace usages of JUnit 4's @Rule ExpectedException with JUnit 5 Assertions.assertThrows
34+
*
35+
* Currently only supports migration of this method from ExpectedException: void expect(Class<? extends Throwable> type)
36+
* Migrating the other methods of ExpectedException is not yet implemented.
37+
*
38+
*/
39+
public class ExpectedExceptionToAssertThrows extends JavaIsoRefactorVisitor {
40+
41+
private static final String ExpectedExceptionFqn = "org.junit.rules.ExpectedException";
42+
43+
public ExpectedExceptionToAssertThrows() {
44+
setCursoringOn();
45+
}
46+
47+
@Override
48+
public J.ClassDecl visitClassDecl(J.ClassDecl classDecl) {
49+
J.ClassDecl cd = super.visitClassDecl(classDecl);
50+
List<J.VariableDecls> expectedExceptionFields = classDecl.findFields(ExpectedExceptionFqn);
51+
if(expectedExceptionFields.size() > 0) {
52+
// Remove the ExpectedException fields
53+
List<J> statements = new ArrayList<>(classDecl.getBody().getStatements());
54+
statements.removeAll(expectedExceptionFields);
55+
cd = cd.withBody(classDecl.getBody().withStatements(statements));
56+
}
57+
return cd;
58+
}
59+
60+
@Override
61+
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method) {
62+
if(method.getType() != null && method.getType().getDeclaringType().getFullyQualifiedName().equals(ExpectedExceptionFqn)) {
63+
J.MethodDecl enclosing = getCursor().firstEnclosing(J.MethodDecl.class);
64+
if(enclosing != null) {
65+
andThen(new Scoped(enclosing, method));
66+
}
67+
}
68+
return super.visitMethodInvocation(method);
69+
}
70+
71+
private static class Scoped extends JavaIsoRefactorVisitor {
72+
private final J.MethodDecl enclosingMethodDecl;
73+
private final J.MethodInvocation expectedExceptionMethod;
74+
75+
private Scoped(J.MethodDecl enclosingMethodDecl, J.MethodInvocation expectedExceptionMethod) {
76+
this.enclosingMethodDecl = enclosingMethodDecl;
77+
this.expectedExceptionMethod = expectedExceptionMethod;
78+
}
79+
80+
@Override
81+
public J.MethodDecl visitMethod(J.MethodDecl m) {
82+
if(!(enclosingMethodDecl.isScope(m) && m.getBody() != null)) {
83+
return m;
84+
}
85+
86+
List<Expression> args = expectedExceptionMethod.getArgs().getArgs();
87+
if(args.size() != 1) {
88+
return m;
89+
}
90+
91+
Expression expectedException = args.get(0);
92+
JavaType.FullyQualified argType = TypeUtils.asFullyQualified(expectedException.getType());
93+
if(argType == null || !argType.getFullyQualifiedName().equals("java.lang.Class")) {
94+
return m;
95+
}
96+
97+
// Remove the ExpectedException.expect() invocation, use the remaining statements as the lambda body for Assertions.assertThrows()
98+
List<Statement> statements = new ArrayList<>(m.getBody().getStatements());
99+
statements.remove(expectedExceptionMethod);
100+
101+
J.MethodInvocation assertThrows = AssertionsBuilder.assertThrows(expectedException, statements);
102+
103+
AddImport addAssertThrows = new AddImport();
104+
addAssertThrows.setType("org.junit.jupiter.api.Assertions");
105+
addAssertThrows.setStaticMethod("assertThrows");
106+
addAssertThrows.setOnlyIfReferenced(false);
107+
andThen(addAssertThrows);
108+
109+
andThen(new AutoFormat(assertThrows));
110+
maybeRemoveImport("org.junit.Rule");
111+
maybeRemoveImport("org.junit.rules.ExpectedException");
112+
113+
m = m.withBody(m.getBody().withStatements(singletonList(assertThrows)));
114+
115+
return m;
116+
}
117+
}
118+
}

src/main/java/org/openrewrite/java/testing/junit5/ChangeTestAnnotation.java renamed to src/main/java/org/openrewrite/java/testing/junit5/UpdateTestAnnotation.java

Lines changed: 3 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
import static org.openrewrite.Tree.randomId;
3838

3939
@AutoConfigure
40-
public class ChangeTestAnnotation extends JavaIsoRefactorVisitor {
41-
public ChangeTestAnnotation() {
40+
public class UpdateTestAnnotation extends JavaIsoRefactorVisitor {
41+
public UpdateTestAnnotation() {
4242
setCursoringOn();
4343
}
4444

@@ -76,68 +76,14 @@ public J.MethodDecl visitMethod(J.MethodDecl method) {
7676
}
7777
if(assignParamName.equals("expected")) {
7878
List<Statement> statements = m.getBody().getStatements();
79-
80-
// The Java 11 Specification says that lambda bodies can be either a single expression
81-
// or a block. So put a block around anything that isn't exactly one expression.
82-
boolean isSingleExpression = statements.size() == 1 && statements.get(0) instanceof Expression;
83-
Statement assertBlock;
84-
if(isSingleExpression) {
85-
assertBlock = statements.get(0).withPrefix(" ");
86-
} else {
87-
assertBlock = new J.Block<>(
88-
randomId(),
89-
null,
90-
statements,
91-
format(" "),
92-
new J.Block.End(randomId(), format("\n"))
93-
);
94-
}
95-
96-
J.MethodInvocation assertThrows = new J.MethodInvocation(
97-
randomId(),
98-
null,
99-
null,
100-
J.Ident.build(randomId(), "assertThrows", JavaType.Primitive.Void, EMPTY),
101-
new J.MethodInvocation.Arguments(
102-
randomId(),
103-
Arrays.asList(
104-
e.withFormatting(EMPTY),
105-
new J.Lambda(
106-
randomId(),
107-
new J.Lambda.Parameters(
108-
randomId(),
109-
true,
110-
Collections.emptyList()
111-
),
112-
new J.Lambda.Arrow(randomId(), format(" ")),
113-
assertBlock,
114-
JavaType.Primitive.Void,
115-
format(" ")
116-
)
117-
),
118-
EMPTY
119-
),
120-
JavaType.Method.build(
121-
JavaType.Class.build("org.junit.jupiter.api.Assertions"),
122-
"assertThrows",
123-
null,
124-
new JavaType.Method.Signature(
125-
new JavaType.GenericTypeVariable("T", JavaType.Class.build("java.lang.Throwable")),
126-
Arrays.asList(JavaType.Class.build("java.lang.Class"), JavaType.Class.build("org.junit.jupiter.api.function.Executable"))),
127-
Arrays.asList("arg0", "arg1"),
128-
new HashSet<>(Arrays.asList(Flag.Public, Flag.Static))
129-
),
130-
format("\n")
131-
);
79+
J.MethodInvocation assertThrows = AssertionsBuilder.assertThrows(e, statements);
13280

13381
AddImport addAssertThrows = new AddImport();
13482
addAssertThrows.setType("org.junit.jupiter.api.Assertions");
13583
addAssertThrows.setStaticMethod("assertThrows");
13684
addAssertThrows.setOnlyIfReferenced(false);
13785
andThen(addAssertThrows);
138-
13986
andThen(new AutoFormat(assertThrows));
140-
andThen(new AutoFormat(assertBlock));
14187

14288
m = method.withBody(m.getBody().withStatements(singletonList(assertThrows)));
14389
} else if (assignParamName.equals("timeout")) {

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,16 @@ visitors:
4949
type: specs.openrewrite.org/v1beta/visitor
5050
name: org.openrewrite.java.testing.junit5.RemoveJUnit4Dependency
5151
visitors:
52-
- org.openrewrite.maven.RemoveDependency
53-
groupId: junit
54-
artifactId: junit
52+
- org.openrewrite.maven.RemoveDependency:
53+
groupId: junit
54+
artifactId: junit
5555
---
5656
type: specs.openrewrite.org/v1beta/visitor
5757
name: org.openrewrite.java.testing.junit5.ExcludeJUnitVintageEngine
5858
visitors:
59-
- org.openrewrite.maven.ExcludeDependency
60-
groupId: org.junit.vintage
61-
artifactId: junit-vintage-engine
59+
- org.openrewrite.maven.ExcludeDependency:
60+
groupId: org.junit.vintage
61+
artifactId: junit-vintage-engine
6262
---
6363
type: specs.openrewrite.org/v1beta/recipe
6464
name: org.openrewrite.java.testing.JUnit5Migration

0 commit comments

Comments
 (0)