Skip to content

Conversation

@MartinWitt
Copy link
Collaborator

After fixing something in SpoonTestshelpers I was shocked to see Hamcrest usage

Copilot AI review requested due to automatic review settings December 28, 2025 15:42
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR refactors test assertions from Hamcrest to AssertJ and improves code readability by using Java text blocks and method references. The main changes include replacing Hamcrest matchers with AssertJ assertions, converting string concatenations to text blocks, and simplifying lambda expressions to method references.

  • Migrated test assertions from Hamcrest assertThat and matchers to AssertJ fluent assertions
  • Converted multi-line string concatenations to Java text blocks for improved readability
  • Simplified lambda expressions to method references where applicable

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 15 comments.

File Description
src/test/java/spoon/test/SpoonTestHelpers.java Converted wrapLocal method to use text block with formatted string
src/test/java/spoon/reflect/visitor/DefaultJavaPrettyPrinterTest.java Replaced all Hamcrest assertions with AssertJ, converted string literal to text block, and simplified lambdas to method references

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 175 to 176
Assertions.assertThat(output).doesNotContain("import java.util.function.Function;");
}
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation uses tabs instead of spaces, which is inconsistent with the rest of the file. The file uses spaces for indentation (4 spaces per level), but this line uses tabs.

Copilot uses AI. Check for mistakes.
Comment on lines 191 to 198
Assertions.assertThat(output)
.contains(" MyClass")
.contains(" MyEnum")
.contains(" MyInterface");

// the code does not contain a 1, which would be the prefix of the local types' binary names
// in the given code snippet
assertThat(output, not(containsString("1")));
Assertions.assertThat(output).doesNotContain("1");
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation uses tabs instead of spaces, which is inconsistent with the rest of the file. The file uses spaces for indentation (4 spaces per level), but these lines use tabs.

Copilot uses AI. Check for mistakes.
Comment on lines 286 to 293
assertThat(sealedType).asString().contains("sealed");
if (explicitPermitted.isEmpty()) {
// the permits keyword should only exist if explicit permitted types are printed
assertThat(printed, not(containsString("permits")));
assertThat(sealedType).asString().doesNotContain("permits");
} else {
assertThat(printed, containsString("permits"));
assertThat(sealedType).asString().contains("permits");
for (String permitted : explicitPermitted) {
assertThat(printed, containsRegexMatch("\\s" + permitted));
assertThat(sealedType).asString().containsPattern("\\s" + permitted);
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation uses tabs instead of spaces, which is inconsistent with the rest of the file. The file uses spaces for indentation (4 spaces per level), but these lines use tabs.

Copilot uses AI. Check for mistakes.
CtClass<?> ctClass = launcher.getFactory().Class().create("MyClass");
ctClass.addModifier(ModifierKind.NON_SEALED);
assertThat(ctClass.toString(), containsString("non-sealed "));
assertThat(ctClass).asString().contains("non-sealed ");
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation uses tabs instead of spaces, which is inconsistent with the rest of the file. The file uses spaces for indentation (4 spaces per level), but this line uses tabs.

Copilot uses AI. Check for mistakes.
Comment on lines 425 to 428
Assertions.assertThat(spoonModelBuilder.build()).isTrue();

CtLocalVariable<Integer> localVariable = model.getElements(new TypeFilter<>(CtLocalVariable.class)).get(1);
assertThat(localVariable.toString(), equalTo("int fieldReadOfA = ((A) c).a.i"));
assertThat(localVariable).asString().isEqualTo("int fieldReadOfA = ((A) c).a.i");
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation uses tabs instead of spaces, which is inconsistent with the rest of the file. The file uses spaces for indentation (4 spaces per level), but these lines use tabs.

Copilot uses AI. Check for mistakes.
Comment on lines +376 to +381
assertThat(x).asString().contains("Set<T>", "List<T> list");

assertThat(x).asString().containsPattern("Collection<.*String>");
assertThat(x).asString().containsPattern("List<.*List<T>>");
assertThat(x).asString().containsPattern("List<.*List<\\? extends T>>");
assertThat(x).asString().containsPattern("List<.*List<\\? super T>>");
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation uses tabs instead of spaces, which is inconsistent with the rest of the file. The file uses spaces for indentation (4 spaces per level), but these lines use tabs.

Copilot uses AI. Check for mistakes.
Comment on lines 399 to 408
Assertions.assertThat(spoonModelBuilder.build()).isTrue();

CtLocalVariable<Integer> localVariable = model.getElements(new TypeFilter<>(CtLocalVariable.class)).get(0);
assertThat(localVariable.toString(), equalTo("int myInt = (int) myDouble"));
assertThat(localVariable).asString().isEqualTo("int myInt = (int) myDouble");

CtLocalVariable<Integer> localVariable2 = model.getElements(new TypeFilter<>(CtLocalVariable.class)).get(1);
assertThat(localVariable2.toString(), equalTo("int myInt2 = ((java.lang.Double) myDouble).intValue()"));
assertThat(localVariable2).asString().isEqualTo("int myInt2 = ((java.lang.Double) myDouble).intValue()");

CtLocalVariable<Integer> localVariable3 = model.getElements(new TypeFilter<>(CtLocalVariable.class)).get(2);
assertThat(localVariable3.toString(), equalTo("double withoutTypeCast = myDoubleObject.doubleValue()"));
assertThat(localVariable3).asString().isEqualTo("double withoutTypeCast = myDoubleObject.doubleValue()");
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation uses tabs instead of spaces, which is inconsistent with the rest of the file. The file uses spaces for indentation (4 spaces per level), but these lines use tabs.

Copilot uses AI. Check for mistakes.
return """
class X {
public void myMethod() {
%s
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The text block format adds unwanted indentation before the substituted content. The original code inserted localDeclarationSnippet directly without any leading spaces, but the new text block has two spaces before %s on line 184. This will add two spaces of indentation to all content passed to this method, which changes the behavior of the function.

Suggested change
%s
%s

Copilot uses AI. Check for mistakes.
complexTypeReference.addActualTypeArgument(complexTypeReference.clone().setSimpleName("Comhyperbola"));
exeExpression.getExecutable().addActualTypeArgument(complexTypeReference);
assertThat(exeExpression.toString(), containsRegexMatch("<(.*)Comparable<(.*)Integer, (.*)Comhyperbola<(.*)Integer>>>"));
assertThat(exeExpression).asString().containsPattern("<(.*)Comparable<(.*)Integer, (.*)Comhyperbola<(.*)Integer>>>");
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation uses tabs instead of spaces, which is inconsistent with the rest of the file. The file uses spaces for indentation (4 spaces per level), but this line uses tabs.

Copilot uses AI. Check for mistakes.

// assert
assertThat(actualStringRepresentation, equalTo("int[] intArray = new int[]{ 3 }"));
assertThat(localVariable).asString().isEqualTo("int[] intArray = new int[]{ 3 }");
Copy link

Copilot AI Dec 28, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The indentation uses tabs instead of spaces, which is inconsistent with the rest of the file. The file uses spaces for indentation (4 spaces per level), but this line uses tabs.

Copilot uses AI. Check for mistakes.
@MartinWitt MartinWitt changed the title refactor: update assertions to use AssertJ and improve readability review: refactor: update assertions to use AssertJ and improve readability Dec 29, 2025
@MartinWitt MartinWitt requested a review from SirYwell December 29, 2025 13:30
@SirYwell
Copy link
Collaborator

Are there more hamcrest usages around somewhere else? In best case, we could remove it at some point.

@MartinWitt
Copy link
Collaborator Author

Are there more hamcrest usages around somewhere else? In best case, we could remove it at some point.

yes but this would end in a monster PR, so I prefer splitted.

@MartinWitt
Copy link
Collaborator Author

Okay looks like we could run mvn -U org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-testing-frameworks:RELEASE -Drewrite.activeRecipes=org.openrewrite.java.testing.hamcrest.MigrateHamcrestToAssertJ -Drewrite.exportDatatables=true and most of hamcrest is gone.

@I-Al-Istannen
Copy link
Collaborator

Okay looks like we could run mvn -U org.openrewrite.maven:rewrite-maven-plugin:run -Drewrite.recipeArtifactCoordinates=org.openrewrite.recipe:rewrite-testing-frameworks:RELEASE -Drewrite.activeRecipes=org.openrewrite.java.testing.hamcrest.MigrateHamcrestToAssertJ -Drewrite.exportDatatables=true and most of hamcrest is gone.

That would probably be nice as a second PR?

Comment on lines -344 to -345
// act
String actualStringRepresentation = localVariable.toString();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think keeping these might be worthwhile for clarity? There are 2-3 of those you inlined into the assert. But no hard feelings from my side.

@monperrus monperrus merged commit 7da1872 into master Jan 18, 2026
16 checks passed
@monperrus
Copy link
Collaborator

thanks @MartinWitt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants