Skip to content

Commit b68748f

Browse files
AlekSimpsontimtebeekknutwannheden
authored
Remove try-catch-fail blocks from unit tests (#370)
* initial commit * updated license header and added test to cover negative case * reworked recipe so that it replace Assert.fail() with Assertions.assertDoesNotThrow() * fixed matcher issue, reworked recipe so that it replaces try-catch block with Assertions.assertDoesNotThrow() block * small fix * making it so that recipe only affect part of method body that contains the try-catch block * small update * switched to JavaVisitor to do J.Try replacement * made changes according to feedback from PR * Fix test import Co-authored-by: Knut Wannheden <[email protected]> * removed MethodDeclaration visitor and unneeded unit test * stashing changes * JavaTemplate issue fixed, updated tests * added test for try with resources blocks and addde precondition for fail() method * Update src/test/java/org/openrewrite/java/testing/junit5/RemoveTryCatchBlocksFromUnitTestsTest.java Co-authored-by: Tim te Beek <[email protected]> * Update src/test/java/org/openrewrite/java/testing/junit5/RemoveTryCatchBlocksFromUnitTestsTest.java Co-authored-by: Tim te Beek <[email protected]> * added extra conditions for recipe to run and updated tests to reflect the added conditions * Rename classes and apply formatter * Update src/test/java/org/openrewrite/java/testing/junit5/RemoveTryCatchFailBlocksFromUnitTestsTest.java Co-authored-by: Tim te Beek <[email protected]> * made changes based on PR feedback * Rename and call super; add test with two catch blocks * Retain fail(String) argument if it's a literal --------- Co-authored-by: Tim te Beek <[email protected]> Co-authored-by: Knut Wannheden <[email protected]>
1 parent 3a58f54 commit b68748f

File tree

2 files changed

+498
-0
lines changed

2 files changed

+498
-0
lines changed
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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 org.openrewrite.ExecutionContext;
19+
import org.openrewrite.Preconditions;
20+
import org.openrewrite.Recipe;
21+
import org.openrewrite.TreeVisitor;
22+
import org.openrewrite.java.JavaParser;
23+
import org.openrewrite.java.JavaTemplate;
24+
import org.openrewrite.java.JavaVisitor;
25+
import org.openrewrite.java.MethodMatcher;
26+
import org.openrewrite.java.search.UsesMethod;
27+
import org.openrewrite.java.tree.Expression;
28+
import org.openrewrite.java.tree.J;
29+
import org.openrewrite.java.tree.Statement;
30+
31+
import java.util.Collections;
32+
import java.util.Set;
33+
34+
public class RemoveTryCatchFailBlocks extends Recipe {
35+
private static final MethodMatcher ASSERT_FAIL_NO_ARG = new MethodMatcher("org.junit.jupiter.api.Assertions fail(..)");
36+
private static final MethodMatcher ASSERT_FAIL_STRING_ARG = new MethodMatcher("org.junit.jupiter.api.Assertions fail(String)");
37+
private static final MethodMatcher GET_MESSAGE_MATCHER = new MethodMatcher("java.lang.Throwable getMessage()");
38+
39+
@Override
40+
public String getDisplayName() {
41+
return "Replace `fail()` in `try-catch` blocks with `Assertions.assertDoesNotThrow(() -> { ... })`";
42+
}
43+
44+
@Override
45+
public String getDescription() {
46+
return "Replace `try-catch` blocks where `catch` merely contains a `fail(..)` statement with " +
47+
"`Assertions.assertDoesNotThrow(() -> { ... })`.";
48+
}
49+
50+
@Override
51+
public Set<String> getTags() {
52+
return Collections.singleton("RSPEC-3658");
53+
}
54+
55+
@Override
56+
public TreeVisitor<?, ExecutionContext> getVisitor() {
57+
return Preconditions.check(new UsesMethod<>("org.junit.jupiter.api.Assertions fail(..)", false), new RemoveTryCatchBlocksFromUnitsTestsVisitor());
58+
}
59+
60+
private static class RemoveTryCatchBlocksFromUnitsTestsVisitor extends JavaVisitor<ExecutionContext> {
61+
@Override
62+
public J visitTry(J.Try jtry, ExecutionContext ctx) {
63+
J.Try try_ = (J.Try) super.visitTry(jtry, ctx);
64+
// only one catch block, such that we know it's safe to apply this recipe, and doesn't have resources
65+
if (try_.getResources() != null || try_.getCatches().size() != 1) {
66+
return try_;
67+
}
68+
69+
/*
70+
Only one statement in the catch block, which is a fail(), with no or a simple String argument.
71+
We would not want to convert for instance fail(cleanUpAndReturnMessage()) might still have side
72+
effects that we don't want to remove.
73+
*/
74+
J.Try.Catch catchBlock = try_.getCatches().get(0);
75+
if (catchBlock.getBody().getStatements().size() != 1) {
76+
return try_;
77+
}
78+
Statement statement = catchBlock.getBody().getStatements().get(0);
79+
if (!(statement instanceof J.MethodInvocation)) {
80+
return try_;
81+
}
82+
J.MethodInvocation failCall = (J.MethodInvocation) statement;
83+
if (!ASSERT_FAIL_NO_ARG.matches(failCall) || !ASSERT_FAIL_STRING_ARG.matches(failCall)) {
84+
return try_;
85+
}
86+
87+
// only valid method that returns string should be getMessage()
88+
if (ASSERT_FAIL_STRING_ARG.matches(failCall)) {
89+
Expression failCallString = failCall.getArguments().get(0);
90+
if (failCallString instanceof J.MethodInvocation && !GET_MESSAGE_MATCHER.matches((J.MethodInvocation) failCallString)) {
91+
return try_;
92+
}
93+
if (failCallString instanceof J.Literal) {
94+
// Retain the fail(String) call argument
95+
maybeAddImport("org.junit.jupiter.api.Assertions");
96+
return JavaTemplate.builder("Assertions.assertDoesNotThrow(() -> #{any()}, #{any(String)})")
97+
.contextSensitive()
98+
.imports("org.junit.jupiter.api.Assertions")
99+
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "junit-jupiter-api-5.9"))
100+
.build()
101+
.apply(getCursor(), try_.getCoordinates().replace(), try_.getBody(), failCallString);
102+
}
103+
}
104+
105+
maybeAddImport("org.junit.jupiter.api.Assertions");
106+
return JavaTemplate.builder("Assertions.assertDoesNotThrow(() -> #{any()})")
107+
.contextSensitive()
108+
.imports("org.junit.jupiter.api.Assertions")
109+
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "junit-jupiter-api-5.9"))
110+
.build()
111+
.apply(getCursor(), try_.getCoordinates().replace(), try_.getBody());
112+
}
113+
}
114+
}

0 commit comments

Comments
 (0)