Skip to content

Commit 7ca74a5

Browse files
Resolve issues with SimplifyChainedAssertJAssertion (#399)
* fixed issue, all tests pass * Document failing test * Do not extract select for method to select argument * Collapse nested if statement * Call hasZeroArgument once only --------- Co-authored-by: Tim te Beek <[email protected]>
1 parent 7574b2f commit 7ca74a5

File tree

4 files changed

+415
-371
lines changed

4 files changed

+415
-371
lines changed

src/main/java/org/openrewrite/java/testing/assertj/SimplifyChainedAssertJAssertion.java

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ public Set<String> getTags() {
7979
@Override
8080
public String getDescription() {
8181
return "Many AssertJ chained assertions have dedicated assertions that function the same. " +
82-
"It is best to use the dedicated assertions.";
82+
"It is best to use the dedicated assertions.";
8383
}
8484

8585
@Override
@@ -108,19 +108,28 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInvocat
108108
}
109109

110110
J.MethodInvocation assertThatArg = (J.MethodInvocation) assertThat.getArguments().get(0);
111-
if (!CHAINED_ASSERT_MATCHER.matches(assertThatArg) && !hasZeroArgument(mi)) {
111+
if (!CHAINED_ASSERT_MATCHER.matches(assertThatArg)) {
112112
return mi;
113113
}
114114

115-
// method call has select
116-
Expression select = assertThatArg.getSelect() != null ? assertThatArg.getSelect() : assertThatArg;
117-
if (!TypeUtils.isAssignableTo(requiredType, select.getType())) {
115+
// Extract the actual argument for the new assertThat call
116+
Expression actual = assertThatArg.getSelect() != null ? assertThatArg.getSelect() : assertThatArg;
117+
if (!TypeUtils.isAssignableTo(requiredType, actual.getType())) {
118118
return mi;
119119
}
120+
List<Expression> arguments = new ArrayList<>();
121+
arguments.add(actual);
120122

121-
List<Expression> arguments = new ArrayList<>(Collections.singletonList(select));
122-
String template = getTemplate(arguments, assertThatArg, mi);
123-
String formattedTemplate = String.format(template, dedicatedAssertion);
123+
// Special case for more expressive assertions: assertThat(x.size()).isEqualTo(0) -> isEmpty()
124+
if ("size".equals(chainedAssertion) && "isEqualTo".equals(assertToReplace) && hasZeroArgument(mi)) {
125+
return applyTemplate("assertThat(#{any()}).isEmpty()", arguments, mi, ctx);
126+
}
127+
128+
String template = getStringTemplateAndAppendArguments(assertThatArg, mi, arguments);
129+
return applyTemplate(String.format(template, dedicatedAssertion), arguments, mi, ctx);
130+
}
131+
132+
private J.MethodInvocation applyTemplate(String formattedTemplate, List<Expression> arguments, J.MethodInvocation mi, ExecutionContext ctx) {
124133
return JavaTemplate.builder(formattedTemplate)
125134
.contextSensitive()
126135
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "junit-jupiter-api-5.9", "assertj-core-3.24"))
@@ -129,33 +138,50 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation methodInvocat
129138
}
130139
}
131140

132-
private String getTemplate(List<Expression> arguments, J.MethodInvocation assertThatArg, J.MethodInvocation methodToReplace) {
133-
if (hasZeroArgument(methodToReplace)) {
141+
private String getStringTemplateAndAppendArguments(J.MethodInvocation assertThatArg, J.MethodInvocation methodToReplace, List<Expression> arguments) {
142+
Expression assertThatArgument = assertThatArg.getArguments().get(0);
143+
Expression methodToReplaceArgument = methodToReplace.getArguments().get(0);
144+
boolean assertThatArgumentIsEmpty = assertThatArgument instanceof J.Empty;
145+
boolean methodToReplaceArgumentIsEmpty = methodToReplaceArgument instanceof J.Empty;
146+
147+
// If both arguments are empty, then the select is already added to the arguments list, and we use a minimal template
148+
if (assertThatArgumentIsEmpty && methodToReplaceArgumentIsEmpty) {
134149
return "assertThat(#{any()}).%s()";
135150
}
136151

137-
if (!(assertThatArg.getArguments().get(0) instanceof J.Empty)
138-
&& !(methodToReplace.getArguments().get(0) instanceof J.Empty)) {
139-
// Note: this should be the only case when more than one argument needs to be handled. When the assertions involve the map functions
140-
arguments.add(assertThatArg.getArguments().get(0));
141-
arguments.add(methodToReplace.getArguments().get(0));
152+
// If both arguments are not empty, then we add both to the arguments to the arguments list, and return a template with two arguments
153+
if (!assertThatArgumentIsEmpty && !methodToReplaceArgumentIsEmpty) {
154+
// This should only happen for map assertions using a key and value
155+
arguments.add(assertThatArgument);
156+
arguments.add(methodToReplaceArgument);
142157
return "assertThat(#{any()}).%s(#{any()}, #{any()})";
143158
}
144159

145-
if (!(assertThatArg.getArguments().get(0) instanceof J.Empty)
146-
|| !(methodToReplace.getArguments().get(0) instanceof J.Empty)) {
147-
Expression argumentToAdd = assertThatArg.getArguments().get(0) instanceof J.Empty ? methodToReplace.getArguments().get(0) : assertThatArg.getArguments().get(0);
148-
argumentToAdd = argumentToAdd instanceof J.MethodInvocation ? ((J.MethodInvocation) argumentToAdd).getSelect() : argumentToAdd;
149-
arguments.add(argumentToAdd);
160+
// If either argument is empty, we choose which one to add to the arguments list, and optionally extract the select
161+
arguments.add(extractEitherArgument(assertThatArgumentIsEmpty, assertThatArgument, methodToReplaceArgument));
150162

151-
if (requiredType.equals("java.nio.file.Path") && dedicatedAssertion.contains("Raw")
152-
&& TypeUtils.isAssignableTo("java.lang.String", assertThatArg.getArguments().get(0).getType())) {
153-
return "assertThat(#{any()}).%s(Path.of(#{any()}))";
154-
}
155-
return "assertThat(#{any()}).%s(#{any()})";
163+
// Special case for Path.of() assertions
164+
if ("java.nio.file.Path".equals(requiredType) && dedicatedAssertion.contains("Raw")
165+
&& TypeUtils.isAssignableTo("java.lang.String", assertThatArgument.getType())) {
166+
return "assertThat(#{any()}).%s(Path.of(#{any()}))";
156167
}
157168

158-
return "assertThat(#{any()}).%s()";
169+
return "assertThat(#{any()}).%s(#{any()})";
170+
171+
}
172+
173+
private static Expression extractEitherArgument(boolean assertThatArgumentIsEmpty, Expression assertThatArgument, Expression methodToReplaceArgument) {
174+
if (assertThatArgumentIsEmpty) {
175+
return methodToReplaceArgument;
176+
}
177+
// Only on the assertThat argument do we possibly replace the argument with the select; such as list.size() -> list
178+
if (assertThatArgument instanceof J.MethodInvocation) {
179+
Expression select = ((J.MethodInvocation) assertThatArgument).getSelect();
180+
if (select != null) {
181+
return select;
182+
}
183+
}
184+
return assertThatArgument;
159185
}
160186

161187
private boolean hasZeroArgument(J.MethodInvocation method) {

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,7 +258,7 @@ recipeList:
258258
- org.openrewrite.java.testing.assertj.SimplifyChainedAssertJAssertion:
259259
chainedAssertion: size
260260
assertToReplace: isEqualTo
261-
dedicatedAssertion: hasSameSizeAs
261+
dedicatedAssertion: hasSize
262262
requiredType: java.util.Map
263263
- org.openrewrite.java.testing.assertj.SimplifyChainedAssertJAssertion:
264264
chainedAssertion: containsKey

0 commit comments

Comments
 (0)