From f0d765a05983b37b93ffa0b9cd44a1c1bb2e79cf Mon Sep 17 00:00:00 2001 From: matusmatokpt <133367802+matusmatokpt@users.noreply.github.com> Date: Tue, 10 Dec 2024 22:32:33 +0100 Subject: [PATCH] Migrate Hamcrest to JUnit 5 (#343) * WIP: Add recipe for migration from Hamcrest Work in progress implementation of a recipe which migrates Hamcrest test matchers to JUnit5 test assertions. Signed-off-by: matus.matok * Add missing license headers * Resolve some of the test issues * Fix test import * Add proto implementation for assertEquals Added a prototype-y implementation of translation from hamcrest's equalTo to JUnit5's assertEquals. Should be easy to add more of the simple hamcrest matchers to this implementation. \TODO the import is not being added Signed-off-by: matus.matok * Use static import and #{any(java.lang.Object)} to fix test * Adapt to main Another iteration of the prototype. Adapted it to be based on the up-to-date main. Added a proposal of how similar simple matchers could be translated to junit assertion methods. Signed-off-by: matus.matok * Add more simple matcher-to-method translations Added translations for matchers closeTo, containsString, empty, emptyArray, emptyIterable, emptyCollectionOf, emptyIterableOf, endsWith. Tests need to be added for each matcher. Signed-off-by: matus.matok * Add more simple matcher-to-method translations Added all the simple matcher-to-method translations. Tests need to be added for each matcher. Signed-off-by: matus.matok * Add tests Added a bunch of tests, to verify the correctness of each matcher-to-assertion case. Signed-off-by: matus.matok * Finalise the pull request Added all the necessary unit tests and polished out the implementation. Signed-off-by: matus.matok * Add required license header * Move classes to align with the Hamcrest to AssertJ implementation * Consistently use `class Test` to avoid conflicts with `@Test` * Refactored and split HamcrestMatcherToJUnit5 recipe Refactored HamcrestMatcherToJUnit5 recipe, so now the whole translation is stored in one place, not scatter amongst three methods. Given the existence of RemoveIsMatcher Recipe, this recipe relies that it will never encounter is() matcher. In similar fashion as RemoveIsMatcher, RemoveNotMatcher was added, which does exactly the same as RemoveIsMatcher, but it also stores the logical context for the nested matcher (so that it knows it was negated) in execution context. Matchers instanceOf and isA were difficult to handle within the HamcrestMatcherToJUnit5 recipe, therefore these cases were moved to a newly added HamcrestInstanceOfToJUnit5 recipe. Signed-off-by: matus.matok * Add license headers Forgot, added now Signed-off-by: matus.matok * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Fix compilation * Update description to use JUnit * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Format tests * Drop RemoveNotMatcher recipe; retain visitor only To avoid misuse * Drop AssertThatBooleanToJUnit5; replace with declarative recipes * Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * Limit execution through preconditions * Add preconditions to HamcrestMatcherToJUnit5 * Minor polish * Extract and reuse `ConsistentHamcrestMatcherImports` * Polish recipe display name and description --------- Signed-off-by: matus.matok Co-authored-by: Tim te Beek Co-authored-by: Tim te Beek Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../hamcrest/HamcrestInstanceOfToJUnit5.java | 116 +++ .../hamcrest/HamcrestMatcherToJUnit5.java | 191 ++++ .../hamcrest/RemoveNotMatcherVisitor.java | 77 ++ .../resources/META-INF/rewrite/hamcrest.yml | 57 +- .../HamcrestInstanceOfToJUnit5Test.java | 128 +++ .../hamcrest/HamcrestMatcherToJUnit5Test.java | 937 ++++++++++++++++++ .../hamcrest/MigrateHamcrestToJUnitTest.java | 149 +++ .../hamcrest/RemoveNotMatcherTest.java | 112 +++ 8 files changed, 1757 insertions(+), 10 deletions(-) create mode 100644 src/main/java/org/openrewrite/java/testing/hamcrest/HamcrestInstanceOfToJUnit5.java create mode 100644 src/main/java/org/openrewrite/java/testing/hamcrest/HamcrestMatcherToJUnit5.java create mode 100644 src/main/java/org/openrewrite/java/testing/hamcrest/RemoveNotMatcherVisitor.java create mode 100644 src/test/java/org/openrewrite/java/testing/hamcrest/HamcrestInstanceOfToJUnit5Test.java create mode 100644 src/test/java/org/openrewrite/java/testing/hamcrest/HamcrestMatcherToJUnit5Test.java create mode 100644 src/test/java/org/openrewrite/java/testing/hamcrest/MigrateHamcrestToJUnitTest.java create mode 100644 src/test/java/org/openrewrite/java/testing/hamcrest/RemoveNotMatcherTest.java diff --git a/src/main/java/org/openrewrite/java/testing/hamcrest/HamcrestInstanceOfToJUnit5.java b/src/main/java/org/openrewrite/java/testing/hamcrest/HamcrestInstanceOfToJUnit5.java new file mode 100644 index 000000000..0fbcdb35f --- /dev/null +++ b/src/main/java/org/openrewrite/java/testing/hamcrest/HamcrestInstanceOfToJUnit5.java @@ -0,0 +1,116 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.java.testing.hamcrest; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaParser; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.search.UsesMethod; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; + +import java.util.ArrayList; +import java.util.List; + +public class HamcrestInstanceOfToJUnit5 extends Recipe { + @Override + public String getDisplayName() { + return "Migrate from Hamcrest `instanceOf` matcher to JUnit 5"; + } + + @Override + public String getDescription() { + return "Migrate from Hamcrest `instanceOf` and `isA` matcher to JUnit5 `assertInstanceOf` assertion."; + } + + private static final MethodMatcher INSTANCE_OF_MATCHER = new MethodMatcher("org.hamcrest.Matchers instanceOf(..)"); + private static final MethodMatcher IS_A_MATCHER = new MethodMatcher("org.hamcrest.Matchers isA(..)"); + private static final MethodMatcher ASSERT_THAT_MATCHER = new MethodMatcher("org.hamcrest.MatcherAssert assertThat(.., org.hamcrest.Matcher)"); + + @Override + public TreeVisitor getVisitor() { + TreeVisitor preconditions = Preconditions.and( + new UsesMethod<>(ASSERT_THAT_MATCHER), + Preconditions.or( + new UsesMethod<>(INSTANCE_OF_MATCHER), + new UsesMethod<>(IS_A_MATCHER))); + return Preconditions.check(preconditions, new JavaIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation mi, ExecutionContext ctx) { + if (ASSERT_THAT_MATCHER.matches(mi)) { + Expression reason; + Expression examinedObject; + Expression hamcrestMatcher; + + if (mi.getArguments().size() == 2) { + reason = null; + examinedObject = mi.getArguments().get(0); + hamcrestMatcher = mi.getArguments().get(1); + } else if (mi.getArguments().size() == 3) { + reason = mi.getArguments().get(0); + examinedObject = mi.getArguments().get(1); + hamcrestMatcher = mi.getArguments().get(2); + } else { + return mi; + } + + J.MethodInvocation matcherInvocation = (J.MethodInvocation) hamcrestMatcher; + while ("not".equals(matcherInvocation.getSimpleName())) { + maybeRemoveImport("org.hamcrest.Matchers.not"); + maybeRemoveImport("org.hamcrest.CoreMatchers.not"); + matcherInvocation = (J.MethodInvocation) new RemoveNotMatcherVisitor().visit(matcherInvocation, ctx); + } + + if (INSTANCE_OF_MATCHER.matches(matcherInvocation) || IS_A_MATCHER.matches(matcherInvocation)) { + boolean logicalContext = RemoveNotMatcherVisitor.getLogicalContext(matcherInvocation, ctx); + + String templateString = (logicalContext ? + "assertInstanceOf(#{any(java.lang.Class)}, #{any(java.lang.Object)}" : + "assertFalse(#{any(java.lang.Class)}.isAssignableFrom(#{any(java.lang.Object)}.getClass())") + + (reason == null ? ")" : ", #{any(java.lang.String)})"); + + JavaTemplate template = JavaTemplate.builder(templateString) + .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "junit-jupiter-api-5.9")) + .staticImports("org.junit.jupiter.api.Assertions." + (logicalContext ? "assertInstanceOf" : "assertFalse")) + .build(); + + maybeRemoveImport("org.hamcrest.MatcherAssert.assertThat"); + maybeRemoveImport("org.hamcrest.Matchers.instanceOf"); + maybeRemoveImport("org.hamcrest.CoreMatchers.instanceOf"); + maybeRemoveImport("org.hamcrest.Matchers.isA"); + maybeRemoveImport("org.hamcrest.CoreMatchers.isA"); + maybeAddImport("org.junit.jupiter.api.Assertions", logicalContext ? "assertInstanceOf" : "assertFalse"); + + List arguments = new ArrayList<>(); + arguments.add(matcherInvocation.getArguments().get(0)); + arguments.add(examinedObject); + if (reason != null) { + arguments.add(reason); + } + + return template.apply(getCursor(), mi.getCoordinates().replace(), arguments.toArray()); + } + } + return super.visitMethodInvocation(mi, ctx); + } + }); + } +} diff --git a/src/main/java/org/openrewrite/java/testing/hamcrest/HamcrestMatcherToJUnit5.java b/src/main/java/org/openrewrite/java/testing/hamcrest/HamcrestMatcherToJUnit5.java new file mode 100644 index 000000000..fe3a9a4dc --- /dev/null +++ b/src/main/java/org/openrewrite/java/testing/hamcrest/HamcrestMatcherToJUnit5.java @@ -0,0 +1,191 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.java.testing.hamcrest; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaParser; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.search.UsesMethod; +import org.openrewrite.java.tree.Expression; +import org.openrewrite.java.tree.J; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiFunction; + +public class HamcrestMatcherToJUnit5 extends Recipe { + + private static final MethodMatcher MATCHER_ASSERT_MATCHER = new MethodMatcher("org.hamcrest.MatcherAssert assertThat(.., org.hamcrest.Matcher)"); + + @Override + public String getDisplayName() { + return "Migrate from Hamcrest `Matcher` to JUnit 5"; + } + + @Override + public String getDescription() { + return "Migrate from Hamcrest `Matcher` to JUnit 5 assertions."; + } + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check( + new UsesMethod<>(MATCHER_ASSERT_MATCHER), + new MigrationFromHamcrestVisitor()); + } + + enum Replacement { + EQUALTO("equalTo", "assertEquals", "assertNotEquals", "#{any(java.lang.Object)}, #{any(java.lang.Object)}", "examinedObjThenMatcherArgs"), + EMPTYARRAY("emptyArray", "assertEquals", "assertNotEquals", "0, #{anyArray(java.lang.Object)}.length", "examinedObjOnly"), + HASENTRY("hasEntry", "assertEquals", "assertNotEquals", "#{any(java.lang.Object)}, #{any(java.util.Map)}.get(#{any(java.lang.Object)})", "matcher1ExaminedObjMatcher0"), + HASSIZE("hasSize", "assertEquals", "assertNotEquals", "#{any(java.util.Collection)}.size(), #{any(double)}", "examinedObjThenMatcherArgs"), + HASTOSTRING("hasToString", "assertEquals", "assertNotEquals", "#{any(java.lang.Object)}.toString(), #{any(java.lang.String)}", "examinedObjThenMatcherArgs"), + CLOSETO("closeTo", "assertTrue", "assertFalse", "Math.abs(#{any(double)} - #{any(double)}) < #{any(double)}", "examinedObjThenMatcherArgs"), + CONTAINSSTRING("containsString", "assertTrue", "assertFalse", "#{any(java.lang.String)}.contains(#{any(java.lang.String)}", "examinedObjThenMatcherArgs"), + EMPTY("empty", "assertTrue", "assertFalse", "#{any(java.util.Collection)}.isEmpty()", "examinedObjOnly"), + ENDSWITH("endsWith", "assertTrue", "assertFalse", "#{any(java.lang.String)}.endsWith(#{any(java.lang.String)})", "examinedObjThenMatcherArgs"), + EQUALTOIGNORINGCASE("equalToIgnoringCase", "assertTrue", "assertFalse", "#{any(java.lang.String)}.equalsIgnoreCase(#{any(java.lang.String)})", "examinedObjThenMatcherArgs"), + GREATERTHAN("greaterThan", "assertTrue", "assertFalse", "#{any(double)} > #{any(double)}", "examinedObjThenMatcherArgs"), + GREATERTHANOREQUALTO("greaterThanOrEqualTo", "assertTrue", "assertFalse", "#{any(double)} >= #{any(double)}", "examinedObjThenMatcherArgs"), + HASKEY("hasKey", "assertTrue", "assertFalse", "#{any(java.util.Map)}.containsKey(#{any(java.lang.Object)})", "examinedObjThenMatcherArgs"), + HASVALUE("hasValue", "assertTrue", "assertFalse", "#{any(java.util.Map)}.containsValue(#{any(java.lang.Object)})", "examinedObjThenMatcherArgs"), + LESSTHAN("lessThan", "assertTrue", "assertFalse", "#{any(double)} < #{any(double)}", "examinedObjThenMatcherArgs"), + LESSTHANOREQUALTO("lessThanOrEqualTo", "assertTrue", "assertFalse", "#{any(double)} <= #{any(double)}", "examinedObjThenMatcherArgs"), + STARTSWITH("startsWith", "assertTrue", "assertFalse", "#{any(java.lang.String)}.startsWith(#{any(java.lang.String)})", "examinedObjThenMatcherArgs"), + TYPECOMPATIBLEWITH("typeCompatibleWith", "assertTrue", "assertFalse", "#{any(java.lang.Class)}.isAssignableFrom(#{any(java.lang.Class)})", "matcherArgsThenExaminedObj"), + NOTNULLVALUE("notNullValue", "assertNotNull", "assertNull", "#{any(java.lang.Object)}", "examinedObjOnly"), + NULLVALUE("nullValue", "assertNull", "assertNotNull", "#{any(java.lang.Object)}", "examinedObjOnly"), + SAMEINSTANCE("sameInstance", "assertSame", "assertNotSame", "#{any(java.lang.Object)}, #{any(java.lang.Object)}", "examinedObjThenMatcherArgs"), + THEINSTANCE("theInstance", "assertSame", "assertNotSame", "#{any(java.lang.Object)}, #{any(java.lang.Object)}", "examinedObjThenMatcherArgs"), + EMPTYITERABLE("emptyIterable", "assertFalse", "assertTrue", "#{any(java.lang.Iterable)}.iterator().hasNext()", "examinedObjOnly"); + + final String hamcrest, junitPositive, junitNegative, template; + final String argumentsMethod; + + private static final Map>> methods = new HashMap<>(); + + static { + methods.put("examinedObjThenMatcherArgs", (ex, matcher) -> { + List arguments = matcher.getArguments(); + arguments.add(0, ex); + return arguments; + }); + methods.put("matcherArgsThenExaminedObj", (ex, matcher) -> { + List arguments = matcher.getArguments(); + arguments.add(ex); + return arguments; + }); + methods.put("examinedObjOnly", (ex, matcher) -> { + List arguments = new ArrayList<>(); + arguments.add(ex); + return arguments; + }); + methods.put("matcher1ExaminedObjMatcher0", (ex, matcher) -> { + List arguments = new ArrayList<>(); + arguments.add(matcher.getArguments().get(1)); + arguments.add(ex); + arguments.add(matcher.getArguments().get(0)); + return arguments; + }); + } + + Replacement(String hamcrest, String junitPositive, String junitNegative, String template, String argumentsMethod) { + this.hamcrest = hamcrest; + this.junitPositive = junitPositive; + this.junitNegative = junitNegative; + this.template = template; + this.argumentsMethod = argumentsMethod; + } + } + + private static class MigrationFromHamcrestVisitor extends JavaIsoVisitor { + + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + J.MethodInvocation mi = super.visitMethodInvocation(method, ctx); + + if (MATCHER_ASSERT_MATCHER.matches(mi)) { + Expression reason; + Expression examinedObject; + Expression hamcrestMatcher; + + if (mi.getArguments().size() == 2) { + reason = null; + examinedObject = mi.getArguments().get(0); + hamcrestMatcher = mi.getArguments().get(1); + } else if (mi.getArguments().size() == 3) { + reason = mi.getArguments().get(0); + examinedObject = mi.getArguments().get(1); + hamcrestMatcher = mi.getArguments().get(2); + } else { + return mi; + } + + if (hamcrestMatcher instanceof J.MethodInvocation) { + J.MethodInvocation matcherInvocation = (J.MethodInvocation) hamcrestMatcher; + maybeRemoveImport("org.hamcrest.MatcherAssert.assertThat"); + + while ("not".equals(matcherInvocation.getSimpleName())) { + maybeRemoveImport("org.hamcrest.Matchers.not"); + maybeRemoveImport("org.hamcrest.CoreMatchers.not"); + matcherInvocation = (J.MethodInvocation) new RemoveNotMatcherVisitor().visit(matcherInvocation, ctx); + } + + //we do not handle nested matchers + if (!(matcherInvocation.getArguments().get(0) instanceof J.Empty)) { + if ((matcherInvocation.getArguments().get(0).getType()).toString().startsWith("org.hamcrest")) { + return mi; + } + } + + boolean logicalContext = RemoveNotMatcherVisitor.getLogicalContext(matcherInvocation, ctx); + + Replacement replacement; + try { + replacement = Replacement.valueOf(matcherInvocation.getSimpleName().toUpperCase()); + } catch (IllegalArgumentException e) { + return mi; + } + String assertion = logicalContext ? replacement.junitPositive : replacement.junitNegative; + String templateString = assertion + "(" + replacement.template + (reason == null ? ")" : ", #{any(java.lang.String)})"); + JavaTemplate template = JavaTemplate.builder(templateString) + .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "junit-jupiter-api-5.9")) + .staticImports("org.junit.jupiter.api.Assertions." + assertion) + .build(); + + maybeRemoveImport("org.hamcrest.Matchers." + replacement.hamcrest); + maybeRemoveImport("org.hamcrest.CoreMatchers." + replacement.hamcrest); + maybeAddImport("org.junit.jupiter.api.Assertions", assertion); + + List arguments = Replacement.methods.get(replacement.argumentsMethod).apply(examinedObject, matcherInvocation); + if (reason != null) { + arguments.add(reason); + } + + return template.apply(getCursor(), method.getCoordinates().replace(), arguments.toArray()); + } + } + return mi; + } + } +} diff --git a/src/main/java/org/openrewrite/java/testing/hamcrest/RemoveNotMatcherVisitor.java b/src/main/java/org/openrewrite/java/testing/hamcrest/RemoveNotMatcherVisitor.java new file mode 100644 index 000000000..cd0413de5 --- /dev/null +++ b/src/main/java/org/openrewrite/java/testing/hamcrest/RemoveNotMatcherVisitor.java @@ -0,0 +1,77 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.java.testing.hamcrest; + +import org.openrewrite.ExecutionContext; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaParser; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.MethodMatcher; +import org.openrewrite.java.tree.J; + +import java.security.InvalidParameterException; +import java.util.Objects; + +class RemoveNotMatcherVisitor extends JavaIsoVisitor { + static final MethodMatcher NOT_MATCHER = new MethodMatcher("org.hamcrest.Matchers not(..)"); + + public static boolean getLogicalContext(J.MethodInvocation mi, ExecutionContext ctx) throws InvalidParameterException { + Object msg = ctx.getMessage(mi.toString()); + if (msg == null) { + return true; + } else if (msg instanceof Boolean) { + return (Boolean) msg; + } else { + throw new InvalidParameterException(); + } + } + + @Override + @SuppressWarnings("ConstantConditions") + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation mi, ExecutionContext ctx) { + if (NOT_MATCHER.matches(mi)) { + boolean logicalContext; + if (ctx.pollMessage(mi.toString()) != null) { + logicalContext = ctx.getMessage(mi.toString()); + } else { + logicalContext = true; + } + + maybeRemoveImport("org.hamcrest.Matchers.not"); + + J.MethodInvocation result; + if (Objects.requireNonNull(mi.getArguments().get(0).getType()).toString().startsWith("org.hamcrest")) { + result = mi.getArguments().get(0).withPrefix(mi.getPrefix()); + } else { + JavaTemplate template = JavaTemplate.builder("equalTo(#{any(java.lang.Object)})") + .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "hamcrest-2.2")) + .staticImports("org.hamcrest.Matchers.equalTo") + .build(); + maybeAddImport("org.hamcrest.Matchers", "equalTo"); + result = template.apply(getCursor(), mi.getCoordinates().replace(), mi.getArguments().get(0)); + } + + ctx.putMessage(result.toString(), !logicalContext); + + return result; + } else { + if (ctx.pollMessage(mi.toString()) == null) { + ctx.putMessage(mi.toString(), true); + } + } + return super.visitMethodInvocation(mi, ctx); + } +} diff --git a/src/main/resources/META-INF/rewrite/hamcrest.yml b/src/main/resources/META-INF/rewrite/hamcrest.yml index 68748c9fa..e08946662 100644 --- a/src/main/resources/META-INF/rewrite/hamcrest.yml +++ b/src/main/resources/META-INF/rewrite/hamcrest.yml @@ -31,8 +31,53 @@ recipeList: acceptTransitive: true --- type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.testing.hamcrest.ConsistentHamcrestMatcherImports +displayName: Use consistent Hamcrest matcher imports +description: Use consistent imports for Hamcrest matchers, and remove wrapping `is(Matcher)` calls ahead of further changes. +tags: + - testing + - hamcrest + - assertj +recipeList: + # First change `is(..)` to `Matchers.is(..)` for consistent matching + - org.openrewrite.java.ChangeMethodTargetToStatic: + methodPattern: org.hamcrest.core.* *(..) + fullyQualifiedTargetTypeName: org.hamcrest.Matchers + - org.openrewrite.java.ChangeMethodTargetToStatic: + methodPattern: org.hamcrest.collection.* *(..) + fullyQualifiedTargetTypeName: org.hamcrest.Matchers + + # Then remove wrapping `is(Matcher)` calls such that further recipes will match + - org.openrewrite.java.testing.hamcrest.RemoveIsMatcher +--- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.testing.hamcrest.MigrateHamcrestToJUnit5 +displayName: Migrate Hamcrest assertions to JUnit Jupiter +description: Migrate Hamcrest `assertThat(..)` to JUnit Jupiter `Assertions`. +tags: + - testing + - hamcrest + - assertj +recipeList: + # First change `is(..)` to `Matchers.is(..)` for consistent matching + - org.openrewrite.java.testing.hamcrest.ConsistentHamcrestMatcherImports + # Then replace `assertThat(String, boolean)` with `assertTrue(boolean, String)` + - org.openrewrite.java.ReorderMethodArguments: + methodPattern: org.hamcrest.MatcherAssert assertThat(java.lang.String, boolean) + oldParameterNames: [reason, assertion] + newParameterNames: [assertion, reason] + - org.openrewrite.java.ChangeMethodName: + methodPattern: org.hamcrest.MatcherAssert assertThat(boolean, String) + newMethodName: assertTrue + - org.openrewrite.java.ChangeMethodTargetToStatic: + methodPattern: org.hamcrest.MatcherAssert assertTrue(boolean, String) + fullyQualifiedTargetTypeName: org.junit.jupiter.api.Assertions + - org.openrewrite.java.testing.hamcrest.HamcrestInstanceOfToJUnit5 + - org.openrewrite.java.testing.hamcrest.HamcrestMatcherToJUnit5 +--- +type: specs.openrewrite.org/v1beta/recipe name: org.openrewrite.java.testing.hamcrest.MigrateHamcrestToAssertJ -displayName: Migrate to AssertJ assertions +displayName: Migrate Hamcrest assertions to AssertJ description: Migrate Hamcrest `assertThat(..)` to AssertJ `Assertions`. tags: - testing @@ -48,15 +93,7 @@ recipeList: acceptTransitive: true # First change `is(..)` to `Matchers.is(..)` for consistent matching - - org.openrewrite.java.ChangeMethodTargetToStatic: - methodPattern: org.hamcrest.core.* *(..) - fullyQualifiedTargetTypeName: org.hamcrest.Matchers - - org.openrewrite.java.ChangeMethodTargetToStatic: - methodPattern: org.hamcrest.collection.* *(..) - fullyQualifiedTargetTypeName: org.hamcrest.Matchers - - # Then remove wrapping `is(Matcher)` calls such that further recipes will match - - org.openrewrite.java.testing.hamcrest.RemoveIsMatcher + - org.openrewrite.java.testing.hamcrest.ConsistentHamcrestMatcherImports # Then remove calls to `MatcherAssert.assertThat(String, is(Matcher))` - org.openrewrite.java.testing.hamcrest.HamcrestIsMatcherToAssertJ diff --git a/src/test/java/org/openrewrite/java/testing/hamcrest/HamcrestInstanceOfToJUnit5Test.java b/src/test/java/org/openrewrite/java/testing/hamcrest/HamcrestInstanceOfToJUnit5Test.java new file mode 100644 index 000000000..a63ec98cd --- /dev/null +++ b/src/test/java/org/openrewrite/java/testing/hamcrest/HamcrestInstanceOfToJUnit5Test.java @@ -0,0 +1,128 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.java.testing.hamcrest; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.InMemoryExecutionContext; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class HamcrestInstanceOfToJUnit5Test implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec + .parser(JavaParser.fromJavaVersion() + .classpathFromResources(new InMemoryExecutionContext(), "junit-jupiter-api-5.9", "hamcrest-2.2")) + .recipe(new HamcrestInstanceOfToJUnit5()); + } + + @DocumentExample + @Test + void instanceOf() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + import java.util.List; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.instanceOf; + import static org.hamcrest.Matchers.isA; + import static org.hamcrest.Matchers.not; + + class ATest { + private static final List list = List.of(); + @Test + void testInstance() { + assertThat(list, instanceOf(Iterable.class)); + assertThat(list, not(instanceOf(Integer.class))); + assertThat(list, isA(Iterable.class)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + import java.util.List; + + import static org.junit.jupiter.api.Assertions.assertFalse; + import static org.junit.jupiter.api.Assertions.assertInstanceOf; + + class ATest { + private static final List list = List.of(); + @Test + void testInstance() { + assertInstanceOf(Iterable.class, list); + assertFalse(Integer.class.isAssignableFrom(list.getClass())); + assertInstanceOf(Iterable.class, list); + } + } + """ + ) + ); + } + + @Test + void assertionsWithReason() { + //language=java + rewriteRun( + java( + """ + import java.util.List; + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.instanceOf; + import static org.hamcrest.Matchers.isA; + import static org.hamcrest.Matchers.not; + + class ATest { + private static final List list = List.of(); + + @Test + void testInstance() { + assertThat("Examined object is not instance of Iterable", list, instanceOf(Iterable.class)); + assertThat("Examined object is not instance of Iterable", list, isA(Iterable.class)); + assertThat("Examined object must not be instance of Integer", list, not(instanceOf(Integer.class))); + } + } + """, + """ + import java.util.List; + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertFalse; + import static org.junit.jupiter.api.Assertions.assertInstanceOf; + + class ATest { + private static final List list = List.of(); + + @Test + void testInstance() { + assertInstanceOf(Iterable.class, list, "Examined object is not instance of Iterable"); + assertInstanceOf(Iterable.class, list, "Examined object is not instance of Iterable"); + assertFalse(Integer.class.isAssignableFrom(list.getClass()), "Examined object must not be instance of Integer"); + } + } + """ + ) + ); + } +} diff --git a/src/test/java/org/openrewrite/java/testing/hamcrest/HamcrestMatcherToJUnit5Test.java b/src/test/java/org/openrewrite/java/testing/hamcrest/HamcrestMatcherToJUnit5Test.java new file mode 100644 index 000000000..42cd23ec9 --- /dev/null +++ b/src/test/java/org/openrewrite/java/testing/hamcrest/HamcrestMatcherToJUnit5Test.java @@ -0,0 +1,937 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.java.testing.hamcrest; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.InMemoryExecutionContext; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class HamcrestMatcherToJUnit5Test implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec + .parser(JavaParser.fromJavaVersion() + .classpathFromResources(new InMemoryExecutionContext(), "junit-jupiter-api-5.9", "hamcrest-2.2")) + .recipe(new HamcrestMatcherToJUnit5()); + } + + @Test + void equalToObject() { + //language=java + rewriteRun( + java( + """ + class Biscuit { + String name; + Biscuit(String name) { + this.name = name; + } + } + """ + ), + java( + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.equalTo; + + class BiscuitTest { + @Test + void testEquals() { + Biscuit theBiscuit = new Biscuit("Ginger"); + Biscuit myBiscuit = new Biscuit("Ginger"); + assertThat(theBiscuit, equalTo(myBiscuit)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertEquals; + + class BiscuitTest { + @Test + void testEquals() { + Biscuit theBiscuit = new Biscuit("Ginger"); + Biscuit myBiscuit = new Biscuit("Ginger"); + assertEquals(theBiscuit, myBiscuit); + } + } + """ + ) + ); + } + + @DocumentExample + @Test + void equalToString() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.equalTo; + + class ATest { + @Test + void testEquals() { + String str1 = "Hello world!"; + String str2 = "Hello world!"; + assertThat(str1, equalTo(str2)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertEquals; + + class ATest { + @Test + void testEquals() { + String str1 = "Hello world!"; + String str2 = "Hello world!"; + assertEquals(str1, str2); + } + } + """ + ) + ); + } + + @Test + void notEqualToString() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.equalTo; + import static org.hamcrest.Matchers.not; + + class ATest { + @Test + void testEquals() { + String str1 = "Hello world!"; + String str2 = "Hello world!"; + assertThat(str1, not(equalTo(str2))); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertNotEquals; + + class ATest { + @Test + void testEquals() { + String str1 = "Hello world!"; + String str2 = "Hello world!"; + assertNotEquals(str1, str2); + } + } + """ + ) + ); + } + + @Test + void greaterThan() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.greaterThan; + + class ATest { + @Test + void testEquals() { + int intt = 7; + assertThat(10, greaterThan(intt)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testEquals() { + int intt = 7; + assertTrue(10 > intt); + } + } + """ + ) + ); + } + + @Test + void greaterThanOrEqualTo() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.greaterThanOrEqualTo; + + class ATest { + @Test + void testGreaterThanOrEqualTo() { + int intt = 7; + assertThat(10, greaterThanOrEqualTo(intt)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testGreaterThanOrEqualTo() { + int intt = 7; + assertTrue(10 >= intt); + } + } + """ + ) + ); + } + + @Test + void closeTo() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.closeTo; + + class ATest { + @Test + void testCloseTo() { + double dbl = 179.1; + assertThat(dbl, closeTo(178.2, 1.0)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testCloseTo() { + double dbl = 179.1; + assertTrue(Math.abs(dbl - 178.2) < 1.0); + } + } + """ + ) + ); + } + + @Test + void collections() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + import java.util.ArrayList; + import java.util.Collection; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.empty; + import static org.hamcrest.Matchers.hasSize; + + class ATest { + private static final Collection collection = new ArrayList<>(); + @Test + void testEmpty() { + assertThat(collection, empty()); + assertThat(collection, hasSize(0)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + import java.util.ArrayList; + import java.util.Collection; + + import static org.junit.jupiter.api.Assertions.assertEquals; + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + private static final Collection collection = new ArrayList<>(); + @Test + void testEmpty() { + assertTrue(collection.isEmpty()); + assertEquals(collection.size(), 0); + } + } + """ + ) + ); + } + + @Test + void arraysAndIterables() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + import java.util.Arrays; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.emptyArray; + import static org.hamcrest.Matchers.emptyIterable; + + class ATest { + private static final Integer[] ints = new Integer[]{}; + @Test + void testEmpty() { + assertThat(ints, emptyArray()); + Iterable iterable = Arrays.stream(ints).toList(); + assertThat(iterable, emptyIterable()); + } + } + """, + """ + import org.junit.jupiter.api.Test; + import java.util.Arrays; + + import static org.junit.jupiter.api.Assertions.assertEquals; + import static org.junit.jupiter.api.Assertions.assertFalse; + + class ATest { + private static final Integer[] ints = new Integer[]{}; + @Test + void testEmpty() { + assertEquals(0, ints.length); + Iterable iterable = Arrays.stream(ints).toList(); + assertFalse(iterable.iterator().hasNext()); + } + } + """ + ) + ); + } + + @Test + void lessThan() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.lessThan; + + class ATest { + @Test + void testLessThan() { + int intt = 7; + assertThat(5, lessThan(intt)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testLessThan() { + int intt = 7; + assertTrue(5 < intt); + } + } + """ + ) + ); + } + + @Test + void lessThanOrEqualTo() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.lessThanOrEqualTo; + + class ATest { + @Test + void testLessThanOrEqualTo() { + int intt = 7; + assertThat(5, lessThanOrEqualTo(intt)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testLessThanOrEqualTo() { + int intt = 7; + assertTrue(5 <= intt); + } + } + """ + ) + ); + } + + @Test + void nullValue() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.nullValue; + import static org.hamcrest.Matchers.notNullValue; + + class ATest { + @Test + void testNullValue() { + Integer integer = null; + String str = "hello world"; + assertThat(integer, nullValue()); + assertThat(str, notNullValue()); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertNotNull; + import static org.junit.jupiter.api.Assertions.assertNull; + + class ATest { + @Test + void testNullValue() { + Integer integer = null; + String str = "hello world"; + assertNull(integer); + assertNotNull(str); + } + } + """ + ) + ); + } + + @Test + void sameInstance() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.not; + import static org.hamcrest.Matchers.sameInstance; + import static org.hamcrest.Matchers.theInstance; + + class ATest { + private final String string = "Hello world."; + @Test + void testSameInstance() { + String localString = string; + String differentString = "Hello void."; + assertThat(string, sameInstance(localString)); + assertThat(string, not(theInstance(differentString))); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertNotSame; + import static org.junit.jupiter.api.Assertions.assertSame; + + class ATest { + private final String string = "Hello world."; + @Test + void testSameInstance() { + String localString = string; + String differentString = "Hello void."; + assertSame(string, localString); + assertNotSame(string, differentString); + } + } + """ + ) + ); + } + + @Test + void hasEntry() { + //language=java + rewriteRun( + java( + """ + import java.util.HashMap; + import java.util.Map; + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.hasEntry; + + class ATest { + @Test + void testHasEntry() { + Map map = new HashMap<>(); + assertThat(map, hasEntry("hello", "world")); + } + } + """, + """ + import java.util.HashMap; + import java.util.Map; + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertEquals; + + class ATest { + @Test + void testHasEntry() { + Map map = new HashMap<>(); + assertEquals("world", map.get("hello")); + } + } + """ + ) + ); + } + + @Test + void hasKey() { + //language=java + rewriteRun( + java( + """ + import java.util.HashMap; + import java.util.Map; + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.hasKey; + + class ATest { + @Test + void testHasKey() { + Map map = new HashMap<>(); + assertThat(map, hasKey("hello")); + } + } + """, + """ + import java.util.HashMap; + import java.util.Map; + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testHasKey() { + Map map = new HashMap<>(); + assertTrue(map.containsKey("hello")); + } + } + """ + ) + ); + } + + @Test + void hasValue() { + //language=java + rewriteRun( + java( + """ + import java.util.HashMap; + import java.util.Map; + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.hasValue; + + class ATest { + @Test + void testHasValue() { + Map map = new HashMap<>(); + assertThat(map, hasValue("world")); + } + } + """, + """ + import java.util.HashMap; + import java.util.Map; + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testHasValue() { + Map map = new HashMap<>(); + assertTrue(map.containsValue("world")); + } + } + """ + ) + ); + } + + @Test + void typeCompatibleWith() { + //language=java + rewriteRun( + java( + """ + import java.util.List; + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.typeCompatibleWith; + + class ATest { + @Test + void testTypeCompatibleWith() { + assertThat(List.class, typeCompatibleWith(Iterable.class)); + } + } + """, + """ + import java.util.List; + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testTypeCompatibleWith() { + assertTrue(Iterable.class.isAssignableFrom(List.class)); + } + } + """ + ) + ); + } + + @Test + void containsString() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.containsString; + + class ATest { + @Test + void testTypeCompatibleWith() { + String string = "hello world"; + String substring = "llo wor"; + assertThat(string, containsString(substring)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testTypeCompatibleWith() { + String string = "hello world"; + String substring = "llo wor"; + assertTrue(string.contains(substring)); + } + } + """ + ) + ); + } + + @Test + void endsWith() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.endsWith; + + class ATest { + @Test + void testTypeCompatibleWith() { + String string = "hello world"; + String suffix = "world"; + assertThat(string, endsWith(suffix)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testTypeCompatibleWith() { + String string = "hello world"; + String suffix = "world"; + assertTrue(string.endsWith(suffix)); + } + } + """ + ) + ); + } + + @Test + void equalToIgnoringCase() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.equalToIgnoringCase; + + class ATest { + @Test + void testTypeCompatibleWith() { + String string1 = "hELLo WoRLD"; + String string2 = "HeLlO WOrLd"; + assertThat(string1, equalToIgnoringCase(string2)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testTypeCompatibleWith() { + String string1 = "hELLo WoRLD"; + String string2 = "HeLlO WOrLd"; + assertTrue(string1.equalsIgnoreCase(string2)); + } + } + """ + ) + ); + } + + @Test + void hasToString() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.hasToString; + + class ATest { + @Test + void testTypeCompatibleWith() { + StringBuilder sb = new StringBuilder(); + sb.append("hello"); + assertThat(sb, hasToString("hello")); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertEquals; + + class ATest { + @Test + void testTypeCompatibleWith() { + StringBuilder sb = new StringBuilder(); + sb.append("hello"); + assertEquals(sb.toString(), "hello"); + } + } + """ + ) + ); + } + + @Test + void startsWith() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.startsWith; + + class ATest { + @Test + void testTypeCompatibleWith() { + String string = "hello world"; + String prefix = "hello"; + assertThat(string, startsWith(prefix)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testTypeCompatibleWith() { + String string = "hello world"; + String prefix = "hello"; + assertTrue(string.startsWith(prefix)); + } + } + """ + ) + ); + } + + @Test + void assertionsWithReason() { + //language=java + rewriteRun( + java( + """ + import java.util.List; + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.startsWith; + + class ATest { + private static final List list = List.of(); + + @Test + void testAssertionsWithReason() { + String string = "hello world"; + String prefix = "hello"; + assertThat("String does not start with given prefix.", string, startsWith(prefix)); + } + } + """, + """ + import java.util.List; + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + private static final List list = List.of(); + + @Test + void testAssertionsWithReason() { + String string = "hello world"; + String prefix = "hello"; + assertTrue(string.startsWith(prefix), "String does not start with given prefix."); + } + } + """ + ) + ); + } + + @Test + void shouldNotRewriteCaseTest() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.containsString; + + class ATest { + @Test + void testTypeCompatibleWith() { + String string = "hello world"; + String substring = "llo wor"; + assertThat(string, containsString(substring)); + assertThat("String does not contain the substring", string.contains(substring)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testTypeCompatibleWith() { + String string = "hello world"; + String substring = "llo wor"; + assertTrue(string.contains(substring)); + assertThat("String does not contain the substring", string.contains(substring)); + } + } + """ + ) + ); + } +} diff --git a/src/test/java/org/openrewrite/java/testing/hamcrest/MigrateHamcrestToJUnitTest.java b/src/test/java/org/openrewrite/java/testing/hamcrest/MigrateHamcrestToJUnitTest.java new file mode 100644 index 000000000..c4a9a49ad --- /dev/null +++ b/src/test/java/org/openrewrite/java/testing/hamcrest/MigrateHamcrestToJUnitTest.java @@ -0,0 +1,149 @@ +/* + * Copyright 2024 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.java.testing.hamcrest; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.InMemoryExecutionContext; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; + +class MigrateHamcrestToJUnitTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec + .parser(JavaParser.fromJavaVersion() + .classpathFromResources(new InMemoryExecutionContext(), "junit-jupiter-api-5.9", "hamcrest-2.2")) + .recipeFromResource("/META-INF/rewrite/hamcrest.yml", "org.openrewrite.java.testing.hamcrest.MigrateHamcrestToJUnit5"); + } + + @DocumentExample + @Test + void equalToString() { + //language=java + rewriteRun( + java( + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.equalTo; + import static org.hamcrest.Matchers.is; + import static org.hamcrest.Matchers.not; + + class ATest { + @Test + void testEquals() { + String str1 = "Hello world!"; + String str2 = "Hello world!"; + assertThat(str1, is(equalTo(str2))); + assertThat(str1, is(not(equalTo(str2 + "!")))); + } + } + """, + """ + import org.junit.jupiter.api.Test; + import static org.junit.jupiter.api.Assertions.assertEquals; + import static org.junit.jupiter.api.Assertions.assertNotEquals; + + class ATest { + @Test + void testEquals() { + String str1 = "Hello world!"; + String str2 = "Hello world!"; + assertEquals(str1, str2); + assertNotEquals(str1, str2 + "!"); + } + } + """ + ) + ); + } + + @Test + void assertWithLogicOp() { + rewriteRun( + //language=java + java( + """ + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + + class ATest { + @Test + void testEquals() { + int a = 7; + int b = 29; + assertThat("Not equal", a == b); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testEquals() { + int a = 7; + int b = 29; + assertTrue(a == b, "Not equal"); + } + } + """ + ) + ); + } + + @Test + void assertWithMethodCall() { + rewriteRun( + //language=java + java( + """ + import org.junit.jupiter.api.Test; + + import static org.hamcrest.MatcherAssert.assertThat; + + class ATest { + @Test + void testContains() { + String string = "Hello world"; + assertThat("Does not contain", string.contains("llo wor")); + } + } + """, + """ + import org.junit.jupiter.api.Test; + + import static org.junit.jupiter.api.Assertions.assertTrue; + + class ATest { + @Test + void testContains() { + String string = "Hello world"; + assertTrue(string.contains("llo wor"), "Does not contain"); + } + } + """ + ) + ); + } +} diff --git a/src/test/java/org/openrewrite/java/testing/hamcrest/RemoveNotMatcherTest.java b/src/test/java/org/openrewrite/java/testing/hamcrest/RemoveNotMatcherTest.java new file mode 100644 index 000000000..418c81a78 --- /dev/null +++ b/src/test/java/org/openrewrite/java/testing/hamcrest/RemoveNotMatcherTest.java @@ -0,0 +1,112 @@ +/* + * Copyright 2023 the original author or authors. + *

+ * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + *

+ * https://www.apache.org/licenses/LICENSE-2.0 + *

+ * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.openrewrite.java.testing.hamcrest; + +import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; +import org.openrewrite.InMemoryExecutionContext; +import org.openrewrite.java.JavaParser; +import org.openrewrite.test.RecipeSpec; +import org.openrewrite.test.RewriteTest; + +import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.test.RewriteTest.toRecipe; + +class RemoveNotMatcherTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec + .parser(JavaParser.fromJavaVersion() + .classpathFromResources(new InMemoryExecutionContext(), "junit-jupiter-api-5.9", "hamcrest-2.2")) + .recipe(toRecipe(RemoveNotMatcherVisitor::new)); + } + + @DocumentExample + void nestedNotMatcher() { + rewriteRun( + //language=java + java( + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.equalTo; + import static org.hamcrest.Matchers.not; + + class ATest { + @Test + void testEquals() { + String str1 = "Hello world!"; + String str2 = "Hello world!"; + assertThat(str1, not(equalTo(str2))); + } + } + """, + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.equalTo; + + class ATest { + @Test + void testEquals() { + String str1 = "Hello world!"; + String str2 = "Hello world!"; + assertThat(str1, equalTo(str2)); + } + } + """ + ) + ); + } + + @Test + void notMatcher() { + rewriteRun( + //language=java + java( + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.not; + + class ATest { + @Test + void testEquals() { + String str1 = "Hello world!"; + String str2 = "Hello world!"; + assertThat(str1, not(str2)); + } + } + """, + """ + import org.junit.jupiter.api.Test; + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.Matchers.equalTo; + + class ATest { + @Test + void testEquals() { + String str1 = "Hello world!"; + String str2 = "Hello world!"; + assertThat(str1, equalTo(str2)); + } + } + """ + ) + ); + } + +}