Skip to content

Commit b2e5a27

Browse files
Migrate Hamcrest anyOf matchers to AssertJ satisfiesAnyOf assertions (#391)
* initial commit, test almost passes * Various fixes Add missing license headers to fix `gradlew build` and CI pipeline Add precondition on use of `anyOf`, to selectively apply visitor Use JavaIsoVisitor, since we're not changing returned type Add TODO for also-matched `anyOf(Iterable)` Rename confusingly named argument variable `select` to `actual` `satisfiesAnyOf` accepts `Consumer`s with an argument, not `Supplier`s Maybe remove import of "org.hamcrest.Matchers.anyOf" Clean up unused imports in expected output * added more tests, recipe accounts for anyOf(Iterable), and more * Expect double tab continuation indent * Apply formatter * Slight polish Use Collectors.joining & parameters.addAll Split build up of template into distinct parts Inline or extract variables where it makes sense Adjust test expectations in terms of indentation * Demonstrate issue with Iterable * Correctly skip `anyOf(Iterable)` * Include HamcrestAnyOfToAssertJ in Hamcrest to AssertJ migration --------- Co-authored-by: Tim te Beek <[email protected]>
1 parent 9b39ea2 commit b2e5a27

File tree

3 files changed

+303
-0
lines changed

3 files changed

+303
-0
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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.hamcrest;
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.JavaIsoVisitor;
23+
import org.openrewrite.java.JavaParser;
24+
import org.openrewrite.java.JavaTemplate;
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.TypeUtils;
30+
31+
import java.util.ArrayList;
32+
import java.util.List;
33+
import java.util.stream.Collectors;
34+
35+
@SuppressWarnings("NullableProblems")
36+
public class HamcrestAnyOfToAssertJ extends Recipe {
37+
@Override
38+
public String getDisplayName() {
39+
return "Migrate `anyOf` Hamcrest Matcher to AssertJ";
40+
}
41+
42+
@Override
43+
public String getDescription() {
44+
return "Migrate the `anyOf` Hamcrest Matcher to AssertJ's `satisfiesAnyOf` assertion.";
45+
}
46+
47+
private static final MethodMatcher ASSERT_THAT_MATCHER = new MethodMatcher("org.hamcrest.MatcherAssert assertThat(..)");
48+
private static final MethodMatcher ANY_OF_MATCHER = new MethodMatcher("org.hamcrest.Matchers anyOf(..)");
49+
50+
@Override
51+
public TreeVisitor<?, ExecutionContext> getVisitor() {
52+
return Preconditions.check(new UsesMethod<>(ANY_OF_MATCHER), new AnyOfToAssertJVisitor());
53+
}
54+
55+
private static class AnyOfToAssertJVisitor extends JavaIsoVisitor<ExecutionContext> {
56+
57+
@Override
58+
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInvocation, ExecutionContext ctx) {
59+
J.MethodInvocation mi = super.visitMethodInvocation(methodInvocation, ctx);
60+
List<Expression> arguments = mi.getArguments();
61+
Expression anyOfExpression = arguments.get(arguments.size() - 1);
62+
if (!ASSERT_THAT_MATCHER.matches(mi) || !ANY_OF_MATCHER.matches(anyOfExpression)) {
63+
return mi;
64+
}
65+
66+
// Skip anyOf(Iterable)
67+
List<Expression> anyOfArguments = ((J.MethodInvocation) anyOfExpression).getArguments();
68+
if (TypeUtils.isAssignableTo("java.lang.Iterable", anyOfArguments.get(0).getType())) {
69+
return mi;
70+
}
71+
72+
StringBuilder template = new StringBuilder();
73+
List<Expression> parameters = new ArrayList<>();
74+
75+
// assertThat(actual)
76+
template.append("assertThat(#{any()})\n");
77+
parameters.add(arguments.get(arguments.size() - 2));
78+
79+
// .as("...")
80+
if (arguments.size() == 3) {
81+
template.append(".as(#{any(java.lang.String)})\n");
82+
parameters.add(arguments.get(0));
83+
}
84+
85+
// .satisfiesAnyOf(...)
86+
template.append(".satisfiesAnyOf(\n");
87+
template.append(anyOfArguments.stream()
88+
.map(arg -> "arg -> assertThat(arg, #{any()})")
89+
.collect(Collectors.joining(",\n")));
90+
parameters.addAll(anyOfArguments);
91+
template.append("\n);");
92+
93+
maybeRemoveImport("org.hamcrest.Matchers.anyOf");
94+
maybeAddImport("org.assertj.core.api.Assertions", "assertThat");
95+
return JavaTemplate.builder(template.toString())
96+
.contextSensitive()
97+
.staticImports("org.assertj.core.api.Assertions.assertThat")
98+
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx,
99+
"assertj-core-3.24",
100+
"hamcrest-2.2",
101+
"junit-jupiter-api-5.9"))
102+
.build()
103+
.apply(getCursor(), mi.getCoordinates().replace(), parameters.toArray());
104+
}
105+
}
106+
}

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,9 @@ recipeList:
4848
# Flatten calls to `MatcherAssert.assertThat(.., allOf(..))` to easier to migrate individual statements
4949
- org.openrewrite.java.testing.hamcrest.FlattenAllOf
5050

51+
# Then remove calls to `MatcherAssert.assertThat(String, anyOf(..))`
52+
- org.openrewrite.java.testing.hamcrest.HamcrestAnyOfToAssertJ
53+
5154
# Then remove calls to `MatcherAssert.assertThat(String, boolean)`
5255
- org.openrewrite.java.testing.hamcrest.AssertThatBooleanToAssertJ
5356

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
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.hamcrest;
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 HamcrestAnyOfToAssertJTest implements RewriteTest {
27+
@Override
28+
public void defaults(RecipeSpec spec) {
29+
spec
30+
.parser(JavaParser.fromJavaVersion().classpathFromResources(new InMemoryExecutionContext(),
31+
"junit-jupiter-api-5.9",
32+
"hamcrest-2.2",
33+
"assertj-core-3.24"))
34+
.recipe(new HamcrestAnyOfToAssertJ());
35+
}
36+
37+
@Test
38+
void anyOfMigrate() {
39+
//language=java
40+
rewriteRun(
41+
java(
42+
"""
43+
import org.junit.jupiter.api.Test;
44+
45+
import static org.hamcrest.MatcherAssert.assertThat;
46+
import static org.hamcrest.Matchers.anyOf;
47+
import static org.hamcrest.Matchers.equalTo;
48+
import static org.hamcrest.Matchers.hasLength;
49+
50+
class MyTest {
51+
@Test
52+
void testMethod() {
53+
assertThat("hello world", anyOf(equalTo("hello world"), hasLength(12)));
54+
}
55+
}
56+
""",
57+
"""
58+
import org.junit.jupiter.api.Test;
59+
60+
import static org.assertj.core.api.Assertions.assertThat;
61+
import static org.hamcrest.MatcherAssert.assertThat;
62+
import static org.hamcrest.Matchers.equalTo;
63+
import static org.hamcrest.Matchers.hasLength;
64+
65+
class MyTest {
66+
@Test
67+
void testMethod() {
68+
assertThat("hello world")
69+
.satisfiesAnyOf(
70+
arg -> assertThat(arg, equalTo("hello world")),
71+
arg -> assertThat(arg, hasLength(12))
72+
);
73+
}
74+
}
75+
"""
76+
)
77+
);
78+
}
79+
80+
@Test
81+
void anyOfHasALotOfArguments() {
82+
//language=java
83+
rewriteRun(
84+
java(
85+
"""
86+
import org.junit.jupiter.api.Test;
87+
88+
import static org.hamcrest.MatcherAssert.assertThat;
89+
import static org.hamcrest.Matchers.anyOf;
90+
import static org.hamcrest.Matchers.equalTo;
91+
import static org.hamcrest.Matchers.hasLength;
92+
93+
class MyTest {
94+
@Test
95+
void testMethod() {
96+
assertThat("hello world", anyOf(equalTo("hello world"), hasLength(12), hasLength(12), hasLength(12), hasLength(12)));
97+
}
98+
}
99+
""",
100+
"""
101+
import org.junit.jupiter.api.Test;
102+
103+
import static org.assertj.core.api.Assertions.assertThat;
104+
import static org.hamcrest.MatcherAssert.assertThat;
105+
import static org.hamcrest.Matchers.equalTo;
106+
import static org.hamcrest.Matchers.hasLength;
107+
108+
class MyTest {
109+
@Test
110+
void testMethod() {
111+
assertThat("hello world")
112+
.satisfiesAnyOf(
113+
arg -> assertThat(arg, equalTo("hello world")),
114+
arg -> assertThat(arg, hasLength(12)),
115+
arg -> assertThat(arg, hasLength(12)),
116+
arg -> assertThat(arg, hasLength(12)),
117+
arg -> assertThat(arg, hasLength(12))
118+
);
119+
}
120+
}
121+
"""
122+
)
123+
);
124+
}
125+
126+
@Test
127+
void anyOfArgumentIsIterable() {
128+
//language=java
129+
rewriteRun(
130+
java(
131+
"""
132+
import org.junit.jupiter.api.Test;
133+
134+
import java.util.Arrays;
135+
136+
import static org.hamcrest.MatcherAssert.assertThat;
137+
import static org.hamcrest.Matchers.hasLength;
138+
import static org.hamcrest.Matchers.anyOf;
139+
140+
class MyTest {
141+
@Test
142+
void testMethod() {
143+
assertThat("hello world", anyOf(Arrays.asList(hasLength(11), hasLength(11))));
144+
}
145+
}
146+
"""
147+
)
148+
);
149+
}
150+
151+
@Test
152+
void assertThatHasReasonArgument() {
153+
//language=java
154+
rewriteRun(
155+
java(
156+
"""
157+
import org.junit.jupiter.api.Test;
158+
159+
import static org.hamcrest.MatcherAssert.assertThat;
160+
import static org.hamcrest.Matchers.anyOf;
161+
import static org.hamcrest.Matchers.equalTo;
162+
import static org.hamcrest.Matchers.hasLength;
163+
164+
class MyTest {
165+
@Test
166+
void testMethod() {
167+
assertThat("reason", "hello world", anyOf(equalTo("hello world"), hasLength(12)));
168+
}
169+
}
170+
""",
171+
"""
172+
import org.junit.jupiter.api.Test;
173+
174+
import static org.assertj.core.api.Assertions.assertThat;
175+
import static org.hamcrest.MatcherAssert.assertThat;
176+
import static org.hamcrest.Matchers.equalTo;
177+
import static org.hamcrest.Matchers.hasLength;
178+
179+
class MyTest {
180+
@Test
181+
void testMethod() {
182+
assertThat("hello world")
183+
.as("reason")
184+
.satisfiesAnyOf(
185+
arg -> assertThat(arg, equalTo("hello world")),
186+
arg -> assertThat(arg, hasLength(12))
187+
);
188+
}
189+
}
190+
"""
191+
)
192+
);
193+
}
194+
}

0 commit comments

Comments
 (0)