diff --git a/build.gradle.kts b/build.gradle.kts index 6e03b6a63..42276ebba 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -43,6 +43,7 @@ dependencies { testImplementation("org.openrewrite:rewrite-java-17") testImplementation("org.openrewrite:rewrite-groovy") + testImplementation("org.openrewrite:rewrite-kotlin:$rewriteVersion") testImplementation("org.openrewrite.gradle.tooling:model:$rewriteVersion") testRuntimeOnly("org.gradle:gradle-tooling-api:latest.release") @@ -54,6 +55,7 @@ dependencies { testRuntimeOnly("net.datafaker:datafaker:latest.release") { exclude(group = "org.yaml", module = "snakeyaml") } + testRuntimeOnly("org.mockito.kotlin:mockito-kotlin:latest.release") testRuntimeOnly("org.testcontainers:testcontainers:latest.release") testRuntimeOnly("org.testcontainers:nginx:latest.release") diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cdaec8840..acacbc504 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,7 +1,7 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionSha256Sum=544c35d6bd849ae8a5ed0bcea39ba677dc40f49df7d1835561582da2009b961d +distributionSha256Sum=a4b4158601f8636cdeeab09bd76afb640030bb5b144aafe261a5e8af027dc612 diff --git a/gradlew b/gradlew index 1aa94a426..b740cf133 100755 --- a/gradlew +++ b/gradlew @@ -55,7 +55,7 @@ # Darwin, MinGW, and NonStop. # # (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt # within the Gradle project. # # You can find Gradle at https://github.com/gradle/gradle/. diff --git a/settings.gradle.kts b/settings.gradle.kts index 00db38549..991d06956 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -8,31 +8,34 @@ pluginManagement { rootProject.name = "rewrite-testing-frameworks" plugins { - id("com.gradle.enterprise") version "latest.release" + id("com.gradle.develocity") version "latest.release" id("com.gradle.common-custom-user-data-gradle-plugin") version "latest.release" } -gradleEnterprise { - val isCiServer = System.getenv("CI")?.equals("true") ?: false +develocity { server = "https://ge.openrewrite.org/" + val isCiServer = System.getenv("CI")?.equals("true") ?: false + val accessKey = System.getenv("GRADLE_ENTERPRISE_ACCESS_KEY") + val authenticated = !accessKey.isNullOrBlank() buildCache { - remote(gradleEnterprise.buildCache) { + remote(develocity.buildCache) { isEnabled = true - val accessKey = System.getenv("GRADLE_ENTERPRISE_ACCESS_KEY") - isPush = isCiServer && !accessKey.isNullOrBlank() + isPush = isCiServer && authenticated } } buildScan { capture { - isTaskInputFiles = true + fileFingerprints = true } - isUploadInBackground = !isCiServer - - publishAlways() - this as com.gradle.enterprise.gradleplugin.internal.extension.BuildScanExtensionWithHiddenFeatures - publishIfAuthenticated() + publishing { + onlyIf { + authenticated + } + } + + uploadInBackground = !isCiServer } } diff --git a/src/main/java/org/openrewrite/java/testing/assertj/IsEqualToEmptyString.java b/src/main/java/org/openrewrite/java/testing/assertj/IsEqualToEmptyString.java new file mode 100644 index 000000000..fb014fd94 --- /dev/null +++ b/src/main/java/org/openrewrite/java/testing/assertj/IsEqualToEmptyString.java @@ -0,0 +1,68 @@ +/* + * 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.assertj; + +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.MethodMatcher; +import org.openrewrite.java.search.UsesMethod; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaType; + +import java.util.Collections; + +/** + * AssertJ has a more idiomatic way of asserting that a String is empty. + * This recipe will find instances of `assertThat(String).isEqualTo("")` and replace them with `isEmpty()`. + */ +public class IsEqualToEmptyString extends Recipe { + + private static final MethodMatcher IS_EQUAL_TO = new MethodMatcher("org.assertj.core.api.AbstractStringAssert isEqualTo(java.lang.String)"); + + @Override + public String getDisplayName() { + return "Convert `assertThat(String).isEqualTo(\"\")` to `isEmpty()`"; + } + + @Override + public String getDescription() { + return "Adopt idiomatic AssertJ assertion for empty Strings."; + } + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check( + new UsesMethod<>(IS_EQUAL_TO), + new JavaIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) { + J.MethodInvocation mi = super.visitMethodInvocation(method, ctx); + if (IS_EQUAL_TO.matches(mi) && J.Literal.isLiteralValue(mi.getArguments().get(0), "")) { + JavaType.Method isEmptyMethodType = mi.getMethodType().withName("isEmpty"); + return mi + .withName(mi.getName().withSimpleName("isEmpty").withType(isEmptyMethodType)) + .withMethodType(isEmptyMethodType) + .withArguments(Collections.emptyList()); + } + return mi; + } + } + ); + } +} diff --git a/src/main/java/org/openrewrite/java/testing/assertj/JUnitFailToAssertJFail.java b/src/main/java/org/openrewrite/java/testing/assertj/JUnitFailToAssertJFail.java index d2f1e7118..b93ee3c9e 100644 --- a/src/main/java/org/openrewrite/java/testing/assertj/JUnitFailToAssertJFail.java +++ b/src/main/java/org/openrewrite/java/testing/assertj/JUnitFailToAssertJFail.java @@ -149,6 +149,7 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu ); //Make sure there is a static import for "org.assertj.core.api.Assertions.assertThat" (even if not referenced) maybeAddImport("org.assertj.core.api.Assertions", "fail", false); + maybeRemoveImport("org.junit.jupiter.api.Assertions.fail"); return super.visitMethodInvocation(method, ctx); } } diff --git a/src/main/java/org/openrewrite/java/testing/cleanup/RemoveTestPrefix.java b/src/main/java/org/openrewrite/java/testing/cleanup/RemoveTestPrefix.java index f16fe570d..2cb18b734 100644 --- a/src/main/java/org/openrewrite/java/testing/cleanup/RemoveTestPrefix.java +++ b/src/main/java/org/openrewrite/java/testing/cleanup/RemoveTestPrefix.java @@ -31,6 +31,7 @@ import java.time.Duration; import java.util.Arrays; import java.util.List; +import java.util.concurrent.atomic.AtomicBoolean; public class RemoveTestPrefix extends Recipe { @@ -121,6 +122,21 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration method, } } + // Skip when calling a similarly named method + AtomicBoolean skip = new AtomicBoolean(false); + new JavaIsoVisitor() { + @Override + public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, AtomicBoolean atomicBoolean) { + if (method.getName().getSimpleName().equals(newMethodName)) { + skip.set(true); + } + return super.visitMethodInvocation(method, atomicBoolean); + } + }.visitMethodDeclaration(m, skip); + if (skip.get()) { + return m; + } + // Rename method and return type = type.withName(newMethodName); return m.withName(m.getName().withSimpleName(newMethodName).withType(type)) diff --git a/src/main/java/org/openrewrite/java/testing/cleanup/TestsShouldNotBePublic.java b/src/main/java/org/openrewrite/java/testing/cleanup/TestsShouldNotBePublic.java index 3f8974a21..7ea4850ce 100644 --- a/src/main/java/org/openrewrite/java/testing/cleanup/TestsShouldNotBePublic.java +++ b/src/main/java/org/openrewrite/java/testing/cleanup/TestsShouldNotBePublic.java @@ -17,25 +17,27 @@ import lombok.AllArgsConstructor; import lombok.EqualsAndHashCode; +import lombok.RequiredArgsConstructor; +import lombok.Value; import org.openrewrite.ExecutionContext; import org.openrewrite.Option; -import org.openrewrite.Recipe; +import org.openrewrite.ScanningRecipe; import org.openrewrite.TreeVisitor; import org.openrewrite.internal.ListUtils; import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.ChangeMethodAccessLevelVisitor; import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.MethodMatcher; -import org.openrewrite.java.tree.*; +import org.openrewrite.java.tree.Comment; +import org.openrewrite.java.tree.Flag; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.TypeUtils; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; -import java.util.Set; +import java.util.*; @AllArgsConstructor @EqualsAndHashCode(callSuper = false) -public class TestsShouldNotBePublic extends Recipe { +public class TestsShouldNotBePublic extends ScanningRecipe { @Option(displayName = "Remove protected modifiers", description = "Also remove protected modifiers from test methods", @@ -60,16 +62,37 @@ public Set getTags() { } @Override - public TreeVisitor getVisitor() { - return new TestsNotPublicVisitor(Boolean.TRUE.equals(removeProtectedModifiers)); + public Accumulator getInitialValue(ExecutionContext ctx) { + return new Accumulator(); } + @Override + public TreeVisitor getScanner(Accumulator acc) { + return new JavaIsoVisitor() { + @Override + public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDeclaration, ExecutionContext ctx) { + J.ClassDeclaration cd = super.visitClassDeclaration(classDeclaration, ctx); + if (cd.getExtends() != null) { + acc.extendedClasses.add(String.valueOf(cd.getExtends().getType())); + } + return cd; + } + }; + } + + @Override + public TreeVisitor getVisitor(Accumulator acc) { + return new TestsNotPublicVisitor(Boolean.TRUE.equals(removeProtectedModifiers), acc); + } + + public static class Accumulator { + Set extendedClasses = new HashSet<>(); + } + + @RequiredArgsConstructor private static final class TestsNotPublicVisitor extends JavaIsoVisitor { private final Boolean orProtected; - - private TestsNotPublicVisitor(Boolean orProtected) { - this.orProtected = orProtected; - } + private final Accumulator acc; @Override public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) { @@ -77,8 +100,8 @@ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, Ex if (c.getKind() != J.ClassDeclaration.Kind.Type.Interface && c.getModifiers().stream().anyMatch(mod -> mod.getType() == J.Modifier.Type.Public) - && c.getModifiers().stream().noneMatch(mod -> mod.getType() == J.Modifier.Type.Abstract)) { - + && c.getModifiers().stream().noneMatch(mod -> mod.getType() == J.Modifier.Type.Abstract) + && !acc.extendedClasses.contains(String.valueOf(c.getType()))) { boolean hasTestMethods = c.getBody().getStatements().stream() .filter(org.openrewrite.java.tree.J.MethodDeclaration.class::isInstance) .map(J.MethodDeclaration.class::cast) diff --git a/src/main/java/org/openrewrite/java/testing/jmockit/ArgumentMatchersRewriter.java b/src/main/java/org/openrewrite/java/testing/jmockit/ArgumentMatchersRewriter.java index 7d5efe007..8b24ee034 100644 --- a/src/main/java/org/openrewrite/java/testing/jmockit/ArgumentMatchersRewriter.java +++ b/src/main/java/org/openrewrite/java/testing/jmockit/ArgumentMatchersRewriter.java @@ -60,6 +60,7 @@ class ArgumentMatchersRewriter { } private static final Map PRIMITIVE_TO_MOCKITO_ARGUMENT_MATCHER = new HashMap<>(); + static { PRIMITIVE_TO_MOCKITO_ARGUMENT_MATCHER.put(JavaType.Primitive.Int, "anyInt"); PRIMITIVE_TO_MOCKITO_ARGUMENT_MATCHER.put(JavaType.Primitive.Long, "anyLong"); @@ -81,7 +82,7 @@ class ArgumentMatchersRewriter { this.expectationsBlock = expectationsBlock; } - J.Block rewriteExpectationsBlock() { + J.Block rewriteJMockitBlock() { List newStatements = new ArrayList<>(expectationsBlock.getStatements().size()); for (Statement expectationStatement : expectationsBlock.getStatements()) { // for each statement, check if it's a method invocation and replace any argument matchers diff --git a/src/main/java/org/openrewrite/java/testing/jmockit/JMockitAnnotatedArgumentToMockito.java b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitAnnotatedArgumentToMockito.java new file mode 100644 index 000000000..a89e16e89 --- /dev/null +++ b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitAnnotatedArgumentToMockito.java @@ -0,0 +1,108 @@ +/* + * 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.jmockit; + +import lombok.AllArgsConstructor; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; +import org.openrewrite.ExecutionContext; +import org.openrewrite.Preconditions; +import org.openrewrite.Recipe; +import org.openrewrite.TreeVisitor; +import org.openrewrite.internal.ListUtils; +import org.openrewrite.java.JavaIsoVisitor; +import org.openrewrite.java.JavaParser; +import org.openrewrite.java.JavaTemplate; +import org.openrewrite.java.search.FindAnnotations; +import org.openrewrite.java.search.UsesType; +import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.Statement; + +import java.util.ArrayList; +import java.util.List; + +@EqualsAndHashCode(callSuper = false) +public class JMockitAnnotatedArgumentToMockito extends Recipe { + @Override + public String getDisplayName() { + return "Convert JMockit `@Mocked` and `@Injectable` annotated arguments"; + } + + @Override + public String getDescription() { + return "Convert JMockit `@Mocked` and `@Injectable` annotated arguments into Mockito statements."; + } + + @Override + public TreeVisitor getVisitor() { + return Preconditions.check( + Preconditions.or( + new UsesType<>("mockit.Mocked", false), + new UsesType<>("mockit.Injectable", false) + ), + new JavaIsoVisitor() { + @Override + public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration methodDeclaration, ExecutionContext ctx) { + J.MethodDeclaration md = super.visitMethodDeclaration(methodDeclaration, ctx); + + List parameters = md.getParameters(); + if (!parameters.isEmpty() && !(parameters.get(0) instanceof J.Empty)) { + maybeRemoveImport("mockit.Injectable"); + maybeRemoveImport("mockit.Mocked"); + maybeAddImport("org.mockito.Mockito"); + + // Create lists to store the mocked parameters and the new type parameters + List mockedParameter = new ArrayList<>(); + + // Remove any mocked parameters from the method declaration + md = md.withParameters(ListUtils.map(parameters, parameter -> { + if (parameter instanceof J.VariableDeclarations) { + J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) parameter; + // Check if the parameter has the annotation "mockit.Mocked or mockit.Injectable" + if (!FindAnnotations.find(variableDeclarations, "mockit.Injectable").isEmpty() || + !FindAnnotations.find(variableDeclarations, "mockit.Mocked").isEmpty() ) { + mockedParameter.add(variableDeclarations); + return null; + } + } + return parameter; + })); + + // Add mocked parameters as statements to the method declaration + if (!mockedParameter.isEmpty()) { + JavaTemplate addStatementsTemplate = JavaTemplate.builder("#{} #{} = Mockito.mock(#{}.class);\n") + .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12")) + .imports("org.mockito.Mockito") + .contextSensitive() + .build(); + // Retain argument order by iterating in reverse + for (int i = mockedParameter.size() - 1; i >= 0; i--) { + J.VariableDeclarations variableDeclarations = mockedParameter.get(i); + // Apply the template and update the method declaration + md = addStatementsTemplate.apply(updateCursor(md), + md.getBody().getCoordinates().firstStatement(), + variableDeclarations.getTypeExpression().toString(), + variableDeclarations.getVariables().get(0).getSimpleName(), + variableDeclarations.getTypeExpression().toString()); + } + } + } + return md; + } + } + ); + } +} diff --git a/src/main/java/org/openrewrite/java/testing/jmockit/ExpectationsBlockRewriter.java b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockRewriter.java similarity index 58% rename from src/main/java/org/openrewrite/java/testing/jmockit/ExpectationsBlockRewriter.java rename to src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockRewriter.java index c45b0b798..147bcdd50 100644 --- a/src/main/java/org/openrewrite/java/testing/jmockit/ExpectationsBlockRewriter.java +++ b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockRewriter.java @@ -15,8 +15,12 @@ */ package org.openrewrite.java.testing.jmockit; +import lombok.AccessLevel; +import lombok.Data; +import lombok.Setter; import org.openrewrite.Cursor; import org.openrewrite.ExecutionContext; +import org.openrewrite.internal.lang.Nullable; import org.openrewrite.java.JavaParser; import org.openrewrite.java.JavaTemplate; import org.openrewrite.java.JavaVisitor; @@ -25,13 +29,16 @@ import java.util.ArrayList; import java.util.List; -class ExpectationsBlockRewriter { +import static org.openrewrite.java.testing.jmockit.JMockitBlockType.Verifications; + +class JMockitBlockRewriter { private static final String WHEN_TEMPLATE_PREFIX = "when(#{any()})."; private static final String RETURN_TEMPLATE_PREFIX = "thenReturn("; private static final String THROW_TEMPLATE_PREFIX = "thenThrow("; private static final String LITERAL_TEMPLATE_FIELD = "#{}"; private static final String ANY_TEMPLATE_FIELD = "#{any()}"; + private static final String MOCKITO_IMPORT_FQN_PREFX = "org.mockito.Mockito"; private static String getObjectTemplateField(String fqn) { return "#{any(" + fqn + ")}"; @@ -40,134 +47,109 @@ private static String getObjectTemplateField(String fqn) { private final JavaVisitor visitor; private final ExecutionContext ctx; private final J.NewClass newExpectations; + private final JMockitBlockType blockType; // index of the Expectations block in the method body private final int bodyStatementIndex; private J.Block methodBody; private JavaCoordinates nextStatementCoordinates; - private boolean expectationsRewriteFailed = false; + private boolean rewriteFailed = false; - boolean isExpectationsRewriteFailed() { - return expectationsRewriteFailed; + boolean isRewriteFailed() { + return rewriteFailed; } // keep track of the additional statements being added to the method body, which impacts the statement indices // used with bodyStatementIndex to obtain the coordinates of the next statement to be written private int numStatementsAdded = 0; - ExpectationsBlockRewriter(JavaVisitor visitor, ExecutionContext ctx, J.Block methodBody, - J.NewClass newExpectations, int bodyStatementIndex) { + JMockitBlockRewriter(JavaVisitor visitor, ExecutionContext ctx, J.Block methodBody, + J.NewClass newExpectations, int bodyStatementIndex, JMockitBlockType blockType) { this.visitor = visitor; this.ctx = ctx; this.methodBody = methodBody; this.newExpectations = newExpectations; this.bodyStatementIndex = bodyStatementIndex; + this.blockType = blockType; nextStatementCoordinates = newExpectations.getCoordinates().replace(); } J.Block rewriteMethodBody() { - visitor.maybeRemoveImport("mockit.Expectations"); + visitor.maybeRemoveImport(blockType.getFqn()); // eg mockit.Expectations assert newExpectations.getBody() != null; - J.Block expectationsBlock = (J.Block) newExpectations.getBody().getStatements().get(0); - if (expectationsBlock.getStatements().isEmpty()) { + J.Block jmockitBlock = (J.Block) newExpectations.getBody().getStatements().get(0); + if (jmockitBlock.getStatements().isEmpty()) { // empty Expectations block, remove it - removeExpectationsStatement(); + removeBlock(); return methodBody; } // rewrite the argument matchers in the expectations block - ArgumentMatchersRewriter amr = new ArgumentMatchersRewriter(visitor, ctx, expectationsBlock); - expectationsBlock = amr.rewriteExpectationsBlock(); - - // iterate over the expectations statements and rebuild the method body - List expectationStatements = new ArrayList<>(); - for (Statement expectationStatement : expectationsBlock.getStatements()) { - if (expectationStatement instanceof J.MethodInvocation) { - // handle returns statements - J.MethodInvocation invocation = (J.MethodInvocation) expectationStatement; - if (invocation.getSelect() == null && invocation.getName().getSimpleName().equals("returns")) { - expectationStatements.add(expectationStatement); - continue; + ArgumentMatchersRewriter amr = new ArgumentMatchersRewriter(visitor, ctx, jmockitBlock); + jmockitBlock = amr.rewriteJMockitBlock(); + + // iterate over the statements and build a list of grouped method invocations and related statements eg times + List> methodInvocationsToRewrite = new ArrayList<>(); + int methodInvocationIdx = -1; + for (Statement jmockitBlockStatement : jmockitBlock.getStatements()) { + if (jmockitBlockStatement instanceof J.MethodInvocation) { + // ensure it's not a returns statement, we add that later to related statements + J.MethodInvocation invocation = (J.MethodInvocation) jmockitBlockStatement; + if (invocation.getSelect() != null && !invocation.getName().getSimpleName().equals("returns")) { + methodInvocationIdx++; + methodInvocationsToRewrite.add(new ArrayList<>()); } - // if a new method invocation is found, apply the template to the previous statements - if (!expectationStatements.isEmpty()) { - // apply template to build new method body - rewriteMethodBody(expectationStatements); + } - // reset statements for next expectation - expectationStatements = new ArrayList<>(); - } + if (methodInvocationIdx != -1) { + methodInvocationsToRewrite.get(methodInvocationIdx).add(jmockitBlockStatement); } - expectationStatements.add(expectationStatement); } - // handle the last statement - if (!expectationStatements.isEmpty()) { - rewriteMethodBody(expectationStatements); + // remove the jmockit block + if (nextStatementCoordinates.isReplacement()) { + removeBlock(); } + // now rewrite + methodInvocationsToRewrite.forEach(this::rewriteMethodInvocation); return methodBody; } - private void rewriteMethodBody(List expectationStatements) { - final MockInvocationResults mockInvocationResults = buildMockInvocationResults(expectationStatements); - if (mockInvocationResults == null || !(expectationStatements.get(0) instanceof J.MethodInvocation)) { - // invalid Expectations block, cannot rewrite - expectationsRewriteFailed = true; + private void rewriteMethodInvocation(List statementsToRewrite) { + final MockInvocationResults mockInvocationResults = buildMockInvocationResults(statementsToRewrite); + if (mockInvocationResults == null) { + // invalid block, cannot rewrite + rewriteFailed = true; return; } - J.MethodInvocation invocation = (J.MethodInvocation) expectationStatements.get(0); - if (!mockInvocationResults.getResults().isEmpty()) { - // rewrite the statement to mockito if there are results - rewriteExpectationResult(mockInvocationResults.getResults(), invocation); - } else if (nextStatementCoordinates.isReplacement()) { - // if there are no results and the Expectations block is not yet replaced, remove it - removeExpectationsStatement(); + + J.MethodInvocation invocation = (J.MethodInvocation) statementsToRewrite.get(0); + boolean hasResults = !mockInvocationResults.getResults().isEmpty(); + if (hasResults) { + rewriteResult(invocation, mockInvocationResults.getResults()); } + + boolean hasTimes = false; if (mockInvocationResults.getTimes() != null) { - writeMethodVerification(invocation, mockInvocationResults.getTimes(), "times"); + hasTimes = true; + rewriteVerify(invocation, mockInvocationResults.getTimes(), "times"); } if (mockInvocationResults.getMinTimes() != null) { - writeMethodVerification(invocation, mockInvocationResults.getMinTimes(), "atLeast"); + hasTimes = true; + rewriteVerify(invocation, mockInvocationResults.getMinTimes(), "atLeast"); } if (mockInvocationResults.getMaxTimes() != null) { - writeMethodVerification(invocation, mockInvocationResults.getMaxTimes(), "atMost"); + hasTimes = true; + rewriteVerify(invocation, mockInvocationResults.getMaxTimes(), "atMost"); } - } - - private void rewriteExpectationResult(List results, J.MethodInvocation invocation) { - String template = getMockitoStatementTemplate(results); - if (template == null) { - // invalid template, cannot rewrite - expectationsRewriteFailed = true; - return; + if (!hasResults && !hasTimes) { + rewriteVerify(invocation, null, null); } - visitor.maybeAddImport("org.mockito.Mockito", "when"); - - List templateParams = new ArrayList<>(); - templateParams.add(invocation); - templateParams.addAll(results); - - methodBody = JavaTemplate.builder(template) - .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12")) - .staticImports("org.mockito.Mockito.*") - .build() - .apply( - new Cursor(visitor.getCursor(), methodBody), - nextStatementCoordinates, - templateParams.toArray() - ); - if (!nextStatementCoordinates.isReplacement()) { - numStatementsAdded += 1; - } - - // the next statement coordinates are directly after the most recently written statement - nextStatementCoordinates = methodBody.getStatements().get(bodyStatementIndex + numStatementsAdded) - .getCoordinates().after(); } - private void removeExpectationsStatement() { + private void removeBlock() { methodBody = JavaTemplate.builder("") .javaParser(JavaParser.fromJavaVersion()) .build() @@ -175,41 +157,92 @@ private void removeExpectationsStatement() { new Cursor(visitor.getCursor(), methodBody), nextStatementCoordinates ); + if (bodyStatementIndex == 0) { + nextStatementCoordinates = methodBody.getCoordinates().firstStatement(); + } else { + setNextCoordinatesAfterLastStatementAdded(0); + } + } - // the next statement coordinates are directly after the most recently added statement, or the first statement - // of the test method body if the Expectations block was the first statement - nextStatementCoordinates = bodyStatementIndex == 0 ? methodBody.getCoordinates().firstStatement() : - methodBody.getStatements().get(bodyStatementIndex + numStatementsAdded).getCoordinates().after(); + private void rewriteResult(J.MethodInvocation invocation, List results) { + String template = getWhenTemplate(results); + if (template == null) { + // invalid template, cannot rewrite + rewriteFailed = true; + return; + } + visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, "when"); + + List templateParams = new ArrayList<>(); + templateParams.add(invocation); + templateParams.addAll(results); + + methodBody = rewriteTemplate(template, templateParams, nextStatementCoordinates); + setNextCoordinatesAfterLastStatementAdded(++numStatementsAdded); } - private void writeMethodVerification(J.MethodInvocation invocation, Expression times, String verificationMode) { - String fqn = getInvocationSelectFullyQualifiedClassName(invocation); - if (fqn == null) { + private void rewriteVerify(J.MethodInvocation invocation, @Nullable Expression times, @Nullable String verificationMode) { + if (invocation.getSelect() == null) { // cannot write a verification statement for an invocation without a select field return; } - visitor.maybeAddImport("org.mockito.Mockito", "verify"); - visitor.maybeAddImport("org.mockito.Mockito", verificationMode); List templateParams = new ArrayList<>(); templateParams.add(invocation.getSelect()); - templateParams.add(times); + if (times != null) { + templateParams.add(times); + } templateParams.add(invocation.getName().getSimpleName()); + String verifyTemplate = getVerifyTemplate(invocation.getArguments(), verificationMode, templateParams); + + JavaCoordinates verifyCoordinates; + if (this.blockType == Verifications) { + // for Verifications, replace the Verifications block + verifyCoordinates = nextStatementCoordinates; + } else { + // for Expectations put the verify at the end of the method + verifyCoordinates = methodBody.getCoordinates().lastStatement(); + } + + methodBody = rewriteTemplate(verifyTemplate, templateParams, verifyCoordinates); + if (this.blockType == Verifications) { + setNextCoordinatesAfterLastStatementAdded(++numStatementsAdded); + } + + // do this last making sure rewrite worked and specify hasReference=false because in verify case it cannot find + // the static reference in AddImport class, and getSelect() returns not null + visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, "verify", false); + if (verificationMode != null) { + visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, verificationMode); + } + } + + private void setNextCoordinatesAfterLastStatementAdded(int numStatementsAdded) { + // the next statement coordinates are directly after the most recently written statement, calculated by + // subtracting the removed jmockit block + int nextStatementIdx = bodyStatementIndex + numStatementsAdded - 1; + if (nextStatementIdx >= this.methodBody.getStatements().size()) { + rewriteFailed = true; + } else { + this.nextStatementCoordinates = this.methodBody.getStatements().get(nextStatementIdx).getCoordinates().after(); + } + } - String verifyTemplate = getVerifyTemplate(invocation.getArguments(), fqn, verificationMode, templateParams); - methodBody = JavaTemplate.builder(verifyTemplate) + private J.Block rewriteTemplate(String verifyTemplate, List templateParams, JavaCoordinates + rewriteCoords) { + JavaTemplate.Builder builder = JavaTemplate.builder(verifyTemplate) .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12")) - .staticImports("org.mockito.Mockito.*") - .imports(fqn) + .staticImports("org.mockito.Mockito.*"); + return builder .build() .apply( new Cursor(visitor.getCursor(), methodBody), - methodBody.getCoordinates().lastStatement(), + rewriteCoords, templateParams.toArray() ); } - private static String getMockitoStatementTemplate(List results) { + private static @Nullable String getWhenTemplate(List results) { boolean buildingResults = false; final StringBuilder templateBuilder = new StringBuilder(WHEN_TEMPLATE_PREFIX); for (Expression result : results) { @@ -251,23 +284,27 @@ private static void appendToTemplate(StringBuilder templateBuilder, boolean buil templateBuilder.append(templateField); } - private static String getVerifyTemplate(List arguments, String fqn, String verificationMode, List templateParams) { + private static String getVerifyTemplate(List arguments, @Nullable String + verificationMode, List templateParams) { + StringBuilder templateBuilder = new StringBuilder("verify(#{any()}"); // eg verify(object + if (verificationMode != null) { + templateBuilder.append(", ").append(verificationMode).append("(#{any(int)})"); // eg verify(object, times(2) + } + templateBuilder.append(").#{}("); // eg verify(object, times(2)).method( + if (arguments.isEmpty()) { - return "verify(#{any(" + fqn + ")}, " - + verificationMode - + "(#{any(int)})).#{}();"; + templateBuilder.append(");"); // eg verify(object, times(2)).method(); <- no args + return templateBuilder.toString(); } - StringBuilder templateBuilder = new StringBuilder("verify(#{any(" + fqn + ")}, " - + verificationMode - + "(#{any(int)})).#{}("); + boolean hasArgument = false; - for (Expression argument : arguments) { + for (Expression argument : arguments) { // eg verify(object, times(2).method(anyLong(), anyInt() if (argument instanceof J.Empty) { continue; } else if (argument instanceof J.Literal) { templateBuilder.append(((J.Literal) argument).getValueSource()); } else { - templateBuilder.append("#{any()}"); + templateBuilder.append(ANY_TEMPLATE_FIELD); templateParams.add(argument); } hasArgument = true; @@ -276,11 +313,11 @@ private static String getVerifyTemplate(List arguments, String fqn, if (hasArgument) { templateBuilder.delete(templateBuilder.length() - 2, templateBuilder.length()); } - templateBuilder.append(");"); + templateBuilder.append(");"); // eg verify(object, times(2).method(anyLong(), anyInt()); return templateBuilder.toString(); } - private static MockInvocationResults buildMockInvocationResults(List expectationStatements) { + private static @Nullable MockInvocationResults buildMockInvocationResults(List expectationStatements) { final MockInvocationResults resultWrapper = new MockInvocationResults(); for (int i = 1; i < expectationStatements.size(); i++) { Statement expectationStatement = expectationStatements.get(i); @@ -316,20 +353,19 @@ private static MockInvocationResults buildMockInvocationResults(List return resultWrapper; } - private static String getVariableNameFromAssignment(J.Assignment assignment) { - String name = null; + private static @Nullable String getVariableNameFromAssignment(J.Assignment assignment) { if (assignment.getVariable() instanceof J.Identifier) { - name = ((J.Identifier) assignment.getVariable()).getSimpleName(); + return ((J.Identifier) assignment.getVariable()).getSimpleName(); } else if (assignment.getVariable() instanceof J.FieldAccess) { J.FieldAccess fieldAccess = (J.FieldAccess) assignment.getVariable(); if (fieldAccess.getTarget() instanceof J.Identifier) { - name = fieldAccess.getSimpleName(); + return fieldAccess.getSimpleName(); } } - return name; + return null; } - private static String getPrimitiveTemplateField(JavaType.Primitive primitiveType) { + private static @Nullable String getPrimitiveTemplateField(JavaType.Primitive primitiveType) { switch (primitiveType) { case Boolean: return "#{any(boolean)}"; @@ -356,54 +392,16 @@ private static String getPrimitiveTemplateField(JavaType.Primitive primitiveType } } - private static String getInvocationSelectFullyQualifiedClassName(J.MethodInvocation invocation) { - Expression select = invocation.getSelect(); - if (select == null || select.getType() == null) { - return null; - } - String fqn = null; - if (select.getType() instanceof JavaType.FullyQualified) { - fqn = ((JavaType.FullyQualified) select.getType()).getFullyQualifiedName(); - } - return fqn; - } - + @Data private static class MockInvocationResults { + @Setter(AccessLevel.NONE) private final List results = new ArrayList<>(); private Expression times; private Expression minTimes; private Expression maxTimes; - private List getResults() { - return results; - } - private void addResult(Expression result) { results.add(result); } - - private Expression getTimes() { - return times; - } - - private void setTimes(Expression times) { - this.times = times; - } - - private Expression getMinTimes() { - return minTimes; - } - - private void setMinTimes(Expression minTimes) { - this.minTimes = minTimes; - } - - private Expression getMaxTimes() { - return maxTimes; - } - - private void setMaxTimes(Expression maxTimes) { - this.maxTimes = maxTimes; - } } } diff --git a/src/main/java/org/openrewrite/java/testing/jmockit/JMockitExpectationsToMockito.java b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockToMockito.java similarity index 62% rename from src/main/java/org/openrewrite/java/testing/jmockit/JMockitExpectationsToMockito.java rename to src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockToMockito.java index e841aa033..4283eedf5 100644 --- a/src/main/java/org/openrewrite/java/testing/jmockit/JMockitExpectationsToMockito.java +++ b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockToMockito.java @@ -27,27 +27,31 @@ import org.openrewrite.java.tree.Statement; import java.util.List; +import java.util.Optional; @Value @EqualsAndHashCode(callSuper = false) -public class JMockitExpectationsToMockito extends Recipe { +public class JMockitBlockToMockito extends Recipe { + @Override public String getDisplayName() { - return "Rewrite JMockit Expectations"; + return "Rewrite JMockit Expectations and Verifications"; } @Override public String getDescription() { - return "Rewrites JMockit `Expectations` blocks to Mockito statements."; + return "Rewrites JMockit `Expectations and Verifications` blocks to Mockito statements."; } @Override public TreeVisitor getVisitor() { - return Preconditions.check(new UsesType<>("mockit.Expectations", false), - new RewriteExpectationsVisitor()); + return Preconditions.check(Preconditions.or( + new UsesType<>(JMockitBlockType.Expectations.getFqn(), false), + new UsesType<>(JMockitBlockType.Verifications.getFqn(), false) + ), new RewriteJMockitBlockVisitor()); } - private static class RewriteExpectationsVisitor extends JavaIsoVisitor { + private static class RewriteJMockitBlockVisitor extends JavaIsoVisitor { @Override public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration methodDeclaration, ExecutionContext ctx) { @@ -63,17 +67,19 @@ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration methodDecl int bodyStatementIndex = 0; // iterate over each statement in the method body, find Expectations blocks and rewrite them while (bodyStatementIndex < statements.size()) { - if (!JMockitUtils.isValidExpectationsNewClassStatement(statements.get(bodyStatementIndex))) { - bodyStatementIndex += 1; - continue; - } - ExpectationsBlockRewriter ebr = new ExpectationsBlockRewriter(this, ctx, methodBody, - ((J.NewClass) statements.get(bodyStatementIndex)), bodyStatementIndex); - methodBody = ebr.rewriteMethodBody(); - statements = methodBody.getStatements(); - // if the expectations rewrite failed, skip the next statement - if (ebr.isExpectationsRewriteFailed()) { - bodyStatementIndex += 1; + Statement s = statements.get(bodyStatementIndex); + Optional blockType = JMockitUtils.getJMockitBlock(s); + if (blockType.isPresent()) { + JMockitBlockRewriter blockRewriter = new JMockitBlockRewriter(this, ctx, methodBody, + ((J.NewClass) s), bodyStatementIndex, blockType.get()); + methodBody = blockRewriter.rewriteMethodBody(); + statements = methodBody.getStatements(); + // if the expectations rewrite failed, skip the next statement + if (blockRewriter.isRewriteFailed()) { + bodyStatementIndex++; + } + } else { + bodyStatementIndex++; } } return md.withBody(methodBody); diff --git a/src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockType.java b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockType.java new file mode 100644 index 000000000..e649448a0 --- /dev/null +++ b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockType.java @@ -0,0 +1,29 @@ +/* + * 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.jmockit; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +enum JMockitBlockType { + + Expectations("mockit.Expectations"), + Verifications("mockit.Verifications"); // Add NonStrictExpectations later + + private final String fqn; +} diff --git a/src/main/java/org/openrewrite/java/testing/jmockit/JMockitMockedVariableToMockito.java b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitMockedVariableToMockito.java deleted file mode 100644 index 80e7ce019..000000000 --- a/src/main/java/org/openrewrite/java/testing/jmockit/JMockitMockedVariableToMockito.java +++ /dev/null @@ -1,106 +0,0 @@ -/* - * 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.jmockit; - -import lombok.EqualsAndHashCode; -import lombok.Value; -import org.openrewrite.ExecutionContext; -import org.openrewrite.Preconditions; -import org.openrewrite.Recipe; -import org.openrewrite.TreeVisitor; -import org.openrewrite.internal.ListUtils; -import org.openrewrite.internal.lang.NonNullApi; -import org.openrewrite.java.JavaIsoVisitor; -import org.openrewrite.java.JavaParser; -import org.openrewrite.java.JavaTemplate; -import org.openrewrite.java.search.FindAnnotations; -import org.openrewrite.java.search.UsesType; -import org.openrewrite.java.tree.J; -import org.openrewrite.java.tree.Statement; - -import java.util.ArrayList; -import java.util.List; - -@Value -@EqualsAndHashCode(callSuper = false) -@NonNullApi -public class JMockitMockedVariableToMockito extends Recipe { - @Override - public String getDisplayName() { - return "Rewrite JMockit Mocked Variable"; - } - - @Override - public String getDescription() { - return "Rewrites JMockit `Mocked Variable` to Mockito statements."; - } - - @Override - public TreeVisitor getVisitor() { - return Preconditions.check(new UsesType<>("mockit.Mocked", false), - new RewriteMockedVariableVisitor()); - } - - private static class RewriteMockedVariableVisitor extends JavaIsoVisitor { - @Override - public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration methodDeclaration, ExecutionContext ctx) { - J.MethodDeclaration md = super.visitMethodDeclaration(methodDeclaration, ctx); - - List parameters = md.getParameters(); - if (!parameters.isEmpty() && !(parameters.get(0) instanceof J.Empty)) { - maybeRemoveImport("mockit.Mocked"); - maybeAddImport("org.mockito.Mockito"); - - // Create lists to store the mocked parameters and the new type parameters - List mockedParameter = new ArrayList<>(); - - // Remove any mocked parameters from the method declaration - md = md.withParameters(ListUtils.map(parameters, parameter -> { - if (parameter instanceof J.VariableDeclarations) { - J.VariableDeclarations variableDeclarations = (J.VariableDeclarations) parameter; - // Check if the parameter has the annotation "mockit.Mocked" - if (!FindAnnotations.find(variableDeclarations, "mockit.Mocked").isEmpty()) { - mockedParameter.add(variableDeclarations); - return null; - } - } - return parameter; - })); - - // Add mocked parameters as statements to the method declaration - if (!mockedParameter.isEmpty()) { - JavaTemplate addStatementsTemplate = JavaTemplate.builder("#{} #{} = Mockito.mock(#{}.class);\n") - .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12")) - .imports("org.mockito.Mockito") - .contextSensitive() - .build(); - // Retain argument order by iterating in reverse - for (int i = mockedParameter.size() - 1; i >= 0; i--) { - J.VariableDeclarations variableDeclarations = mockedParameter.get(i); - // Apply the template and update the method declaration - md = addStatementsTemplate.apply(updateCursor(md), - md.getBody().getCoordinates().firstStatement(), - variableDeclarations.getTypeExpression().toString(), - variableDeclarations.getVariables().get(0).getSimpleName(), - variableDeclarations.getTypeExpression().toString()); - } - } - } - - return md; - } - } -} diff --git a/src/main/java/org/openrewrite/java/testing/jmockit/JMockitUtils.java b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitUtils.java index 79d21e91c..a11b5246b 100644 --- a/src/main/java/org/openrewrite/java/testing/jmockit/JMockitUtils.java +++ b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitUtils.java @@ -19,20 +19,32 @@ import org.openrewrite.java.tree.Statement; import org.openrewrite.java.tree.TypeUtils; +import java.util.Optional; + +import static java.util.Optional.empty; + class JMockitUtils { - static boolean isValidExpectationsNewClassStatement(Statement s) { + + static Optional getJMockitBlock(Statement s) { if (!(s instanceof J.NewClass)) { - return false; + return empty(); } J.NewClass nc = (J.NewClass) s; if (!(nc.getClazz() instanceof J.Identifier)) { - return false; + return empty(); } J.Identifier clazz = (J.Identifier) nc.getClazz(); - if (!TypeUtils.isAssignableTo("mockit.Expectations", clazz.getType())) { - return false; + + // JMockit block should be composed of a block within another block + if (nc.getBody() == null || nc.getBody().getStatements().size() != 1) { + return empty(); + } + + for (JMockitBlockType blockType : JMockitBlockType.values()) { + if (TypeUtils.isAssignableTo(blockType.getFqn(), clazz.getType())) { + return Optional.of(blockType); + } } - // Expectations block should be composed of a block within another block - return nc.getBody() != null && nc.getBody().getStatements().size() == 1; + return empty(); } } diff --git a/src/main/java/org/openrewrite/java/testing/jmockit/SetupStatementsRewriter.java b/src/main/java/org/openrewrite/java/testing/jmockit/SetupStatementsRewriter.java index 4fd9a54f2..fbbc1add3 100644 --- a/src/main/java/org/openrewrite/java/testing/jmockit/SetupStatementsRewriter.java +++ b/src/main/java/org/openrewrite/java/testing/jmockit/SetupStatementsRewriter.java @@ -36,9 +36,9 @@ class SetupStatementsRewriter { J.Block rewriteMethodBody() { List statements = methodBody.getStatements(); - // iterate over each statement in the method body, find Expectations blocks and rewrite them + // iterate over each statement in the method body, find JMockit blocks and rewrite them for (Statement s : statements) { - if (!JMockitUtils.isValidExpectationsNewClassStatement(s)) { + if (!JMockitUtils.getJMockitBlock(s).isPresent()) { continue; } J.NewClass nc = (J.NewClass) s; diff --git a/src/main/java/org/openrewrite/java/testing/jmockit/package-info.java b/src/main/java/org/openrewrite/java/testing/jmockit/package-info.java new file mode 100644 index 000000000..85365fe33 --- /dev/null +++ b/src/main/java/org/openrewrite/java/testing/jmockit/package-info.java @@ -0,0 +1,19 @@ +/* + * Copyright 2021 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. + */ +@NonNullApi +package org.openrewrite.java.testing.jmockit; + +import org.openrewrite.internal.lang.NonNullApi; diff --git a/src/main/java/org/openrewrite/java/testing/junit5/AssertToAssertions.java b/src/main/java/org/openrewrite/java/testing/junit5/AssertToAssertions.java index fd9a07044..497713472 100644 --- a/src/main/java/org/openrewrite/java/testing/junit5/AssertToAssertions.java +++ b/src/main/java/org/openrewrite/java/testing/junit5/AssertToAssertions.java @@ -129,7 +129,7 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Execu } private static boolean isJunitAssertMethod(J.MethodInvocation method) { - if (method.getMethodType() != null && TypeUtils.isAssignableTo(ASSERTION_TYPE, method.getMethodType().getDeclaringType())) { + if (method.getMethodType() != null && TypeUtils.isOfType(ASSERTION_TYPE, method.getMethodType().getDeclaringType())) { return !"assertThat".equals(method.getSimpleName()); } if (method.getMethodType() == null && JUNIT_ASSERT_METHOD_NAMES.contains(method.getSimpleName())) { diff --git a/src/main/java/org/openrewrite/java/testing/junit5/CleanupJUnitImports.java b/src/main/java/org/openrewrite/java/testing/junit5/CleanupJUnitImports.java index a411e92b2..77a408c1b 100644 --- a/src/main/java/org/openrewrite/java/testing/junit5/CleanupJUnitImports.java +++ b/src/main/java/org/openrewrite/java/testing/junit5/CleanupJUnitImports.java @@ -22,6 +22,7 @@ import org.openrewrite.java.JavaIsoVisitor; import org.openrewrite.java.search.UsesType; import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaSourceFile; public class CleanupJUnitImports extends Recipe { @Override @@ -44,14 +45,18 @@ public TreeVisitor getVisitor() { public static class CleanupJUnitImportsVisitor extends JavaIsoVisitor { @Override - public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) { - for (J.Import im : cu.getImports()) { - String packageName = im.getPackageName(); - if (packageName.startsWith("junit") || (packageName.startsWith("org.junit") && !packageName.contains("jupiter"))) { - maybeRemoveImport(im.getTypeName()); + public J preVisit(J tree, ExecutionContext ctx) { + stopAfterPreVisit(); + if (tree instanceof JavaSourceFile) { + JavaSourceFile c = (JavaSourceFile) tree; + for (J.Import imp : c.getImports()) { + String packageName = imp.getPackageName(); + if (packageName.startsWith("junit") || (packageName.startsWith("org.junit") && !packageName.contains("jupiter"))) { + maybeRemoveImport(imp.getTypeName()); + } } } - return cu; + return tree; } } } diff --git a/src/main/java/org/openrewrite/java/testing/junit5/LifecycleNonPrivate.java b/src/main/java/org/openrewrite/java/testing/junit5/LifecycleNonPrivate.java index 60f4881fb..273362d04 100644 --- a/src/main/java/org/openrewrite/java/testing/junit5/LifecycleNonPrivate.java +++ b/src/main/java/org/openrewrite/java/testing/junit5/LifecycleNonPrivate.java @@ -39,6 +39,7 @@ public class LifecycleNonPrivate extends Recipe { "org.junit.jupiter.api.AfterEach", "org.junit.jupiter.api.BeforeAll", "org.junit.jupiter.api.BeforeEach"); + @SuppressWarnings("unchecked") private static final TreeVisitor PRECONDITION = Preconditions.or(ANNOTATION_TYPES.stream().map(r -> new UsesType<>(r, false)).toArray(UsesType[]::new)); diff --git a/src/main/java/org/openrewrite/java/testing/junit5/ParameterizedRunnerToParameterized.java b/src/main/java/org/openrewrite/java/testing/junit5/ParameterizedRunnerToParameterized.java index 1a30b6cd1..fa8d51a3e 100644 --- a/src/main/java/org/openrewrite/java/testing/junit5/ParameterizedRunnerToParameterized.java +++ b/src/main/java/org/openrewrite/java/testing/junit5/ParameterizedRunnerToParameterized.java @@ -142,8 +142,10 @@ private static class ParameterizedRunnerToParameterizedTestsVisitor extends Java private final J.ClassDeclaration scope; private final String initMethodName; private final List parameterizedTestMethodParameters; + @Nullable private final List parameterizedTestAnnotationParameters; + private final String initStatementParamString; private final JavaTemplate parameterizedTestTemplate; diff --git a/src/main/java/org/openrewrite/java/testing/junit5/RunnerToExtension.java b/src/main/java/org/openrewrite/java/testing/junit5/RunnerToExtension.java index a736fe378..660f756b6 100644 --- a/src/main/java/org/openrewrite/java/testing/junit5/RunnerToExtension.java +++ b/src/main/java/org/openrewrite/java/testing/junit5/RunnerToExtension.java @@ -38,7 +38,7 @@ public class RunnerToExtension extends Recipe { @Option(displayName = "Runners", description = "The fully qualified class names of the JUnit 4 runners to replace. Sometimes several runners are replaced by a single JUnit Jupiter extension.", - example = "org.springframework.test.context.junit4.SpringRunner") + example = "[ org.springframework.test.context.junit4.SpringRunner ]") List runners; @Option(displayName = "Extension", diff --git a/src/main/java/org/openrewrite/java/testing/junit5/UpdateBeforeAfterAnnotations.java b/src/main/java/org/openrewrite/java/testing/junit5/UpdateBeforeAfterAnnotations.java index 832634e32..1809e14ab 100644 --- a/src/main/java/org/openrewrite/java/testing/junit5/UpdateBeforeAfterAnnotations.java +++ b/src/main/java/org/openrewrite/java/testing/junit5/UpdateBeforeAfterAnnotations.java @@ -49,15 +49,13 @@ public TreeVisitor getVisitor() { public static class UpdateBeforeAfterAnnotationsVisitor extends JavaIsoVisitor { @Override - public J.CompilationUnit visitCompilationUnit(J.CompilationUnit cu, ExecutionContext ctx) { - //This visitor handles changing the method visibility for any method annotated with one of the four before/after - //annotations. It registers visitors that will sweep behind it making the type changes. + public J preVisit(J tree, ExecutionContext ctx) { + stopAfterPreVisit(); doAfterVisit(new ChangeType("org.junit.Before", "org.junit.jupiter.api.BeforeEach", true).getVisitor()); doAfterVisit(new ChangeType("org.junit.After", "org.junit.jupiter.api.AfterEach", true).getVisitor()); doAfterVisit(new ChangeType("org.junit.BeforeClass", "org.junit.jupiter.api.BeforeAll", true).getVisitor()); doAfterVisit(new ChangeType("org.junit.AfterClass", "org.junit.jupiter.api.AfterAll", true).getVisitor()); - - return super.visitCompilationUnit(cu, ctx); + return tree; } } } diff --git a/src/main/java/org/openrewrite/java/testing/mockito/AnyToNullable.java b/src/main/java/org/openrewrite/java/testing/mockito/AnyToNullable.java index 4616bf1b5..d9a201b17 100644 --- a/src/main/java/org/openrewrite/java/testing/mockito/AnyToNullable.java +++ b/src/main/java/org/openrewrite/java/testing/mockito/AnyToNullable.java @@ -20,6 +20,7 @@ import org.openrewrite.java.ChangeMethodName; import org.openrewrite.java.ChangeMethodTargetToStatic; import org.openrewrite.java.tree.J; +import org.openrewrite.java.tree.JavaSourceFile; import org.openrewrite.xml.tree.Xml; import java.util.concurrent.atomic.AtomicBoolean; @@ -63,20 +64,18 @@ public TreeVisitor getScanner(AtomicBoolean acc) { @Override public TreeVisitor getVisitor(AtomicBoolean acc) { - ChangeMethodName changeMethodName = new ChangeMethodName( - "org.mockito.Mockito any(java.lang.Class)", "nullable", null, null); - ChangeMethodTargetToStatic changeMethodTargetToStatic = new ChangeMethodTargetToStatic( - "org.mockito.Mockito nullable(java.lang.Class)", "org.mockito.ArgumentMatchers", null, null); - AnyStringToNullable anyStringToNullable = new AnyStringToNullable(); return Preconditions.check(acc.get(), new TreeVisitor() { @Override - public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) { - if (tree instanceof J) { - doAfterVisit(changeMethodName.getVisitor()); - doAfterVisit(changeMethodTargetToStatic.getVisitor()); - doAfterVisit(anyStringToNullable.getVisitor()); + public @Nullable Tree preVisit(Tree tree, ExecutionContext ctx) { + if (tree instanceof JavaSourceFile) { + stopAfterPreVisit(); + doAfterVisit(new ChangeMethodName( + "org.mockito.Mockito any(java.lang.Class)", "nullable", null, null).getVisitor()); + doAfterVisit(new ChangeMethodTargetToStatic( + "org.mockito.Mockito nullable(java.lang.Class)", "org.mockito.ArgumentMatchers", null, null).getVisitor()); + doAfterVisit(new AnyStringToNullable().getVisitor()); } - return super.visit(tree, ctx); + return super.preVisit(tree, ctx); } }); } diff --git a/src/main/java/org/openrewrite/java/testing/mockito/CleanupMockitoImports.java b/src/main/java/org/openrewrite/java/testing/mockito/CleanupMockitoImports.java index 8f9fcae2e..b8e63c553 100644 --- a/src/main/java/org/openrewrite/java/testing/mockito/CleanupMockitoImports.java +++ b/src/main/java/org/openrewrite/java/testing/mockito/CleanupMockitoImports.java @@ -47,7 +47,9 @@ public String getDescription() { @Override public TreeVisitor getVisitor() { - return Preconditions.check(new UsesType<>("org.mockito.*", false), new CleanupMockitoImportsVisitor()); + return Preconditions.check( + new UsesType<>("org.mockito.*", false), + new CleanupMockitoImportsVisitor()); } private static class CleanupMockitoImportsVisitor extends JavaIsoVisitor { @@ -104,12 +106,18 @@ private static class CleanupMockitoImportsVisitor extends JavaIsoVisitor qualifiedMethods) { J.MethodInvocation mi = super.visitMethodInvocation(method, qualifiedMethods); if (MOCKITO_METHOD_NAMES.contains(mi.getSimpleName()) - && mi.getSelect() != null - && TypeUtils.isAssignableTo("org.mockito.Mockito", mi.getSelect().getType())) { + && mi.getSelect() != null + && TypeUtils.isAssignableTo("org.mockito.Mockito", mi.getSelect().getType())) { qualifiedMethods.add(mi.getSimpleName()); } return mi; diff --git a/src/main/resources/META-INF/rewrite/assertj.yml b/src/main/resources/META-INF/rewrite/assertj.yml index 18cac7ae6..2695a5dd0 100644 --- a/src/main/resources/META-INF/rewrite/assertj.yml +++ b/src/main/resources/META-INF/rewrite/assertj.yml @@ -26,6 +26,7 @@ recipeList: - org.openrewrite.java.testing.assertj.JUnitToAssertj - org.openrewrite.java.testing.assertj.StaticImports - org.openrewrite.java.testing.assertj.SimplifyChainedAssertJAssertions + - org.openrewrite.java.testing.assertj.IsEqualToEmptyString --- type: specs.openrewrite.org/v1beta/recipe diff --git a/src/main/resources/META-INF/rewrite/hamcrest.yml b/src/main/resources/META-INF/rewrite/hamcrest.yml index 997af9b0f..1bcb7a12e 100644 --- a/src/main/resources/META-INF/rewrite/hamcrest.yml +++ b/src/main/resources/META-INF/rewrite/hamcrest.yml @@ -41,7 +41,10 @@ tags: recipeList: # First change `is(..)` to `Matchers.is(..)` for consistent matching - org.openrewrite.java.ChangeMethodTargetToStatic: - methodPattern: org.hamcrest.core.Is is(..) + 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 @@ -275,6 +278,12 @@ recipeList: - org.openrewrite.java.testing.hamcrest.HamcrestNotMatcherToAssertJ: notMatcher: hasItem assertion: doesNotContain + - org.openrewrite.java.testing.hamcrest.HamcrestNotMatcherToAssertJ: + notMatcher: hasItems + assertion: doesNotContain + - org.openrewrite.java.testing.hamcrest.HamcrestNotMatcherToAssertJ: + notMatcher: empty + assertion: isNotEmpty # Add dependency if not already present - org.openrewrite.java.dependencies.AddDependency: diff --git a/src/main/resources/META-INF/rewrite/jmockit.yml b/src/main/resources/META-INF/rewrite/jmockit.yml index 7a32d3b47..4178ebcd4 100644 --- a/src/main/resources/META-INF/rewrite/jmockit.yml +++ b/src/main/resources/META-INF/rewrite/jmockit.yml @@ -22,8 +22,8 @@ tags: - testing - jmockit recipeList: - - org.openrewrite.java.testing.jmockit.JMockitExpectationsToMockito - - org.openrewrite.java.testing.jmockit.JMockitMockedVariableToMockito + - org.openrewrite.java.testing.jmockit.JMockitBlockToMockito + - org.openrewrite.java.testing.jmockit.JMockitAnnotatedArgumentToMockito - org.openrewrite.java.ChangeType: oldFullyQualifiedTypeName: mockit.Mocked newFullyQualifiedTypeName: org.mockito.Mock @@ -42,3 +42,9 @@ recipeList: version: 5.x onlyIfUsing: org.mockito.* acceptTransitive: true + - org.openrewrite.java.dependencies.RemoveDependency: + groupId: org.jmockit + artifactId: jmockit + - org.openrewrite.maven.ExcludeDependency: + groupId: org.jmockit + artifactId: jmockit diff --git a/src/main/resources/META-INF/rewrite/junit5.yml b/src/main/resources/META-INF/rewrite/junit5.yml index 90cf92e59..82396b90b 100755 --- a/src/main/resources/META-INF/rewrite/junit5.yml +++ b/src/main/resources/META-INF/rewrite/junit5.yml @@ -63,6 +63,7 @@ recipeList: pluginArtifactId: maven-surefire-plugin groupId: org.apache.maven.surefire artifactId: surefire-junit* + - org.openrewrite.java.testing.junit5.UpgradeSurefirePlugin - org.openrewrite.java.testing.junit5.UseHamcrestAssertThat - org.openrewrite.java.testing.junit5.MigrateAssumptions - org.openrewrite.java.testing.junit5.UseMockitoExtension @@ -267,7 +268,7 @@ recipeList: oldPackageName: okhttp3.mockwebserver newPackageName: mockwebserver3 recursive: true - - org.openrewrite.maven.ChangeDependencyGroupIdAndArtifactId: + - org.openrewrite.java.dependencies.ChangeDependency: oldGroupId: com.squareup.okhttp3 oldArtifactId: mockwebserver newArtifactId: mockwebserver3-junit5 @@ -288,6 +289,7 @@ recipeList: - org.openrewrite.java.testing.cleanup.AssertFalseEqualsToAssertNotEquals - org.openrewrite.java.testing.cleanup.AssertEqualsNullToAssertNull - org.openrewrite.java.testing.cleanup.AssertFalseNullToAssertNotNull + - org.openrewrite.java.testing.cleanup.AssertTrueNullToAssertNull - org.openrewrite.java.testing.cleanup.AssertEqualsBooleanToAssertBoolean - org.openrewrite.java.testing.cleanup.AssertNotEqualsBooleanToAssertBoolean - org.openrewrite.java.testing.cleanup.AssertionsArgumentOrder @@ -301,10 +303,30 @@ tags: - junit - xmlunit recipeList: - - org.openrewrite.maven.ChangeDependencyGroupIdAndArtifactId: + - org.openrewrite.java.dependencies.ChangeDependency: oldGroupId: xmlunit oldArtifactId: xmlunit newGroupId: org.xmlunit newArtifactId: xmlunit-legacy newVersion: 2.x --- +type: specs.openrewrite.org/v1beta/recipe +name: org.openrewrite.java.testing.junit5.UpgradeSurefirePlugin +displayName: Upgrade Surefire Plugin +description: Upgrades the Maven Surefire Plugin to the latest version if still using an older Maven version. +preconditions: + - org.openrewrite.java.search.HasBuildToolVersion: + type: Maven + version: 0.0.1-3.5.4 +recipeList: + - org.openrewrite.maven.AddPlugin: + groupId: org.apache.maven.plugins + artifactId: maven-surefire-plugin + version: 3.2.5 + - org.openrewrite.maven.AddPluginDependency: + pluginGroupId: org.apache.maven.plugins + pluginArtifactId: maven-surefire-plugin + groupId: org.junit.platform + artifactId: junit-platform-surefire-provider + version: 1.1.0 + diff --git a/src/test/java/org/openrewrite/java/testing/assertj/AssertJBestPracticesTest.java b/src/test/java/org/openrewrite/java/testing/assertj/AssertJBestPracticesTest.java new file mode 100644 index 000000000..4252b33ff --- /dev/null +++ b/src/test/java/org/openrewrite/java/testing/assertj/AssertJBestPracticesTest.java @@ -0,0 +1,61 @@ +/* + * 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.assertj; + +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 AssertJBestPracticesTest implements RewriteTest { + + @Override + public void defaults(RecipeSpec spec) { + spec + .parser(JavaParser.fromJavaVersion().classpathFromResources(new InMemoryExecutionContext(), "assertj-core-3.24")) + .recipeFromResources("org.openrewrite.java.testing.assertj.Assertj"); + } + + @DocumentExample + @Test + void convertsIsEqualToEmptyString() { + rewriteRun( + //language=java + java( + """ + import static org.assertj.core.api.Assertions.assertThat; + class Test { + void test() { + assertThat("test").isEqualTo(""); + } + } + """, + """ + import static org.assertj.core.api.Assertions.assertThat; + class Test { + void test() { + assertThat("test").isEmpty(); + } + } + """ + ) + ); + } +} diff --git a/src/test/java/org/openrewrite/java/testing/cleanup/RemoveTestPrefixTest.java b/src/test/java/org/openrewrite/java/testing/cleanup/RemoveTestPrefixTest.java index 00be3cd29..6cdeae91a 100644 --- a/src/test/java/org/openrewrite/java/testing/cleanup/RemoveTestPrefixTest.java +++ b/src/test/java/org/openrewrite/java/testing/cleanup/RemoveTestPrefixTest.java @@ -53,7 +53,7 @@ void testMethod() { @Test void test_snake_case() { } - + @Test void testRTFCharacters() { } @@ -85,7 +85,7 @@ void method() { @Test void snake_case() { } - + @Test void rtfCharacters() { } @@ -139,7 +139,7 @@ void ignoreOverriddenMethod() { abstract class AbstractTest { public abstract void testMethod(); } - + class BTest extends AbstractTest { @Test @Override @@ -268,7 +268,7 @@ class ATest { @Test void testMyDoSomethingLogic() { } - + void myDoSomethingLogic() {} } """ @@ -292,7 +292,7 @@ class ATest { @MethodSource void testMyDoSomethingLogic(Arguments args) { } - + static Stream testMyDoSomethingLogic() { return Stream.empty(); } @@ -324,4 +324,25 @@ void tests() { ) ); } + + @Test + @Issue("https://github.com/openrewrite/rewrite-testing-frameworks/issues/258") + void removeTestPrefixWithClashingMethod() { + rewriteRun( + //language=java + java( + """ + import org.junit.jupiter.api.Test; + import static java.util.List.of; + + class FooTest { + @Test + void testOf() { + of(); + } + } + """ + ) + ); + } } diff --git a/src/test/java/org/openrewrite/java/testing/cleanup/TestsShouldNotBePublicTest.java b/src/test/java/org/openrewrite/java/testing/cleanup/TestsShouldNotBePublicTest.java index 5241651f4..3d9679549 100644 --- a/src/test/java/org/openrewrite/java/testing/cleanup/TestsShouldNotBePublicTest.java +++ b/src/test/java/org/openrewrite/java/testing/cleanup/TestsShouldNotBePublicTest.java @@ -18,6 +18,7 @@ import org.junit.jupiter.api.Test; import org.openrewrite.DocumentExample; import org.openrewrite.InMemoryExecutionContext; +import org.openrewrite.Issue; import org.openrewrite.java.JavaParser; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; @@ -493,4 +494,42 @@ Collection testFactoryMethod() { ) ); } + + @Test + @Issue("https://github.com/openrewrite/rewrite-testing-frameworks/issues/309") + void baseclassForTestsNeedsToStayPublic() { + //language=java + rewriteRun( + spec -> spec.recipe(new TestsShouldNotBePublic(true)), + java( + // base class for tests should stay public + """ + package com.hello; + + import org.junit.jupiter.api.BeforeEach; + + public class MyTestBase { + @BeforeEach + void setUp() { + } + } + """ + ), + java( + // test class extends base class from another package + """ + package com.world; + + import com.hello.MyTestBase; + import org.junit.jupiter.api.Test; + + class MyTest extends MyTestBase { + @Test + void isWorking() { + } + } + """ + ) + ); + } } diff --git a/src/test/java/org/openrewrite/java/testing/hamcrest/MigrateHamcrestToAssertJTest.java b/src/test/java/org/openrewrite/java/testing/hamcrest/MigrateHamcrestToAssertJTest.java index bc3c30f62..5b44a19c1 100644 --- a/src/test/java/org/openrewrite/java/testing/hamcrest/MigrateHamcrestToAssertJTest.java +++ b/src/test/java/org/openrewrite/java/testing/hamcrest/MigrateHamcrestToAssertJTest.java @@ -522,6 +522,7 @@ void test() { } } """; + @Language("java") private static final String JAVA_AFTER = """ import org.junit.jupiter.api.Test; @@ -718,4 +719,78 @@ void ba() { ) ); } + + @Test + @Issue("https://github.com/openrewrite/rewrite-testing-frameworks/issues/519") + void isEqualMatcherFromCore() { + rewriteRun( + //language=java + java( + """ + import static org.hamcrest.MatcherAssert.assertThat; + import static org.hamcrest.core.IsEqual.equalTo; + import static org.hamcrest.core.IsNot.not; + import static org.hamcrest.core.IsSame.sameInstance; + + import org.junit.jupiter.api.Test; + + class DebugTest { + @Test + void ba() { + assertThat(System.out, equalTo(System.out)); + assertThat(System.out, not(System.out)); + assertThat(System.out, sameInstance(System.out)); + } + } + """, + """ + import static org.assertj.core.api.Assertions.assertThat; + + import org.junit.jupiter.api.Test; + + class DebugTest { + @Test + void ba() { + assertThat(System.out).isEqualTo(System.out); + assertThat(System.out).isNotEqualTo(System.out); + assertThat(System.out).isSameAs(System.out); + } + } + """ + ) + ); + } + + @Test + @Issue("https://github.com/openrewrite/rewrite-testing-frameworks/issues/538") + void collectionMatchers() { + //language=java + rewriteRun( + java( + """ + import java.util.List; + + import static org.hamcrest.collection.IsCollectionWithSize.hasSize; + import static org.hamcrest.MatcherAssert.assertThat; + + class Foo { + void bar(List list) { + assertThat(list, hasSize(12)); + } + } + """, + """ + import java.util.List; + + import static org.assertj.core.api.Assertions.assertThat; + + class Foo { + void bar(List list) { + assertThat(list).hasSize(12); + } + } + """ + ) + ); + } } diff --git a/src/test/java/org/openrewrite/java/testing/jmockit/JMockitMockedVariableToMockitoTest.java b/src/test/java/org/openrewrite/java/testing/jmockit/JMockitAnnotatedArgumentToMockitoTest.java similarity index 59% rename from src/test/java/org/openrewrite/java/testing/jmockit/JMockitMockedVariableToMockitoTest.java rename to src/test/java/org/openrewrite/java/testing/jmockit/JMockitAnnotatedArgumentToMockitoTest.java index 89293f3ef..aba68599f 100644 --- a/src/test/java/org/openrewrite/java/testing/jmockit/JMockitMockedVariableToMockitoTest.java +++ b/src/test/java/org/openrewrite/java/testing/jmockit/JMockitAnnotatedArgumentToMockitoTest.java @@ -24,7 +24,7 @@ import static org.openrewrite.java.Assertions.java; -class JMockitMockedVariableToMockitoTest implements RewriteTest { +class JMockitAnnotatedArgumentToMockitoTest implements RewriteTest { @Override public void defaults(RecipeSpec spec) { spec @@ -32,9 +32,7 @@ public void defaults(RecipeSpec spec) { .logCompilationWarningsAndErrors(true) .classpathFromResources(new InMemoryExecutionContext(), "junit-jupiter-api-5.9", - "jmockit-1.49", - "mockito-core-3.12", - "mockito-junit-jupiter-3.12" + "jmockit-1.49" )) .recipeFromResource( "/META-INF/rewrite/jmockit.yml", @@ -52,7 +50,7 @@ void mockedVariableTest() { import mockit.Mocked; import static org.junit.jupiter.api.Assertions.assertNotNull; - + class A { @Mocked Object mockedObject; @@ -68,7 +66,7 @@ void test(@Mocked Object o, @Mocked Object o2) { import org.mockito.Mockito; import static org.junit.jupiter.api.Assertions.assertNotNull; - + class A { @Mock Object mockedObject; @@ -86,7 +84,7 @@ void test() { } @Test - void noVariableTest() { + void mockedNoVariableTest() { rewriteRun( //language=java java( @@ -94,7 +92,7 @@ void noVariableTest() { import mockit.Mocked; import static org.junit.jupiter.api.Assertions.assertNotNull; - + class A { @Mocked Object mockedObject; @@ -108,7 +106,86 @@ void test() { import org.mockito.Mock; import static org.junit.jupiter.api.Assertions.assertNotNull; - + + class A { + @Mock + Object mockedObject; + + void test() { + assertNotNull(mockedObject); + } + } + """ + ) + ); + } + + @Test + void injectableVariableTest() { + //language=java + rewriteRun( + java( + """ + import mockit.Injectable; + + import static org.junit.jupiter.api.Assertions.assertNotNull; + + class A { + @Injectable + Object mockedObject; + + void test(@Injectable Object o, @Injectable Object o2) { + assertNotNull(o); + assertNotNull(o2); + } + } + """, + """ + import org.mockito.Mock; + import org.mockito.Mockito; + + import static org.junit.jupiter.api.Assertions.assertNotNull; + + class A { + @Mock + Object mockedObject; + + void test() { + Object o = Mockito.mock(Object.class); + Object o2 = Mockito.mock(Object.class); + assertNotNull(o); + assertNotNull(o2); + } + } + """ + ) + ); + } + + @Test + void injectableNoVariableTest() { + rewriteRun( + //language=java + java( + """ + import mockit.Injectable; + + import static org.junit.jupiter.api.Assertions.assertNotNull; + + class A { + @Injectable + Object mockedObject; + + void test() { + assertNotNull(mockedObject); + } + } + """, + """ + import org.mockito.Mock; + + import static org.junit.jupiter.api.Assertions.assertNotNull; + class A { @Mock Object mockedObject; diff --git a/src/test/java/org/openrewrite/java/testing/jmockit/JMockitExpectationsToMockitoTest.java b/src/test/java/org/openrewrite/java/testing/jmockit/JMockitExpectationsToMockitoTest.java index 72d8cfe7d..54d26e64c 100644 --- a/src/test/java/org/openrewrite/java/testing/jmockit/JMockitExpectationsToMockitoTest.java +++ b/src/test/java/org/openrewrite/java/testing/jmockit/JMockitExpectationsToMockitoTest.java @@ -44,7 +44,7 @@ public void defaults(RecipeSpec spec) { @DocumentExample @Test - void voidResult() { + void whenNoResultNoTimes() { //language=java rewriteRun( java( @@ -72,6 +72,8 @@ void test() { import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; + import static org.mockito.Mockito.*; + @ExtendWith(MockitoExtension.class) class MyTest { @Mock @@ -79,6 +81,54 @@ class MyTest { void test() { myObject.wait(10L, 10); + verify(myObject).wait(anyLong(), anyInt()); + } + } + """ + ) + ); + } + + @DocumentExample + @Test + void whenNoResultNoTimesNoArgs() { + //language=java + rewriteRun( + java( + """ + import mockit.Expectations; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + new Expectations() {{ + myObject.wait(); + }}; + myObject.wait(10L, 10); + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.verify; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(10L, 10); + verify(myObject).wait(); } } """ @@ -105,7 +155,7 @@ public String getSomeField() { import mockit.Mocked; import mockit.integration.junit5.JMockitExtension; import org.junit.jupiter.api.extension.ExtendWith; - + import static org.junit.jupiter.api.Assertions.assertNull; @ExtendWith(JMockitExtension.class) @@ -126,7 +176,7 @@ void test() { import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - + import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.when; @@ -159,19 +209,19 @@ public int getSomeField() { """ ), java( - """ + """ import mockit.Expectations; import mockit.Mocked; import mockit.integration.junit5.JMockitExtension; import org.junit.jupiter.api.extension.ExtendWith; - + import static org.junit.jupiter.api.Assertions.assertEquals; - + @ExtendWith(JMockitExtension.class) class MyTest { @Mocked MyObject myObject; - + void test() { new Expectations() {{ myObject.getSomeField(); @@ -190,7 +240,7 @@ void test() { import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; @@ -225,19 +275,19 @@ public String getSomeField(String s) { """ ), java( - """ + """ import mockit.Expectations; import mockit.Mocked; import mockit.integration.junit5.JMockitExtension; import org.junit.jupiter.api.extension.ExtendWith; - + import static org.junit.jupiter.api.Assertions.assertEquals; - + @ExtendWith(JMockitExtension.class) class MyTest { @Mocked MyObject myObject; - + void test() { new Expectations() {{ myObject.getSomeField(anyString); @@ -251,7 +301,7 @@ void test() { import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.when; @@ -285,21 +335,21 @@ public String getSomeField() { """ ), java( - """ + """ import mockit.Expectations; import mockit.Mocked; import mockit.integration.junit5.JMockitExtension; import org.junit.jupiter.api.extension.ExtendWith; - + import static org.junit.jupiter.api.Assertions.assertEquals; - + @ExtendWith(JMockitExtension.class) class MyTest { @Mocked MyObject myObject; - + String expected = "expected"; - + void test() { new Expectations() {{ myObject.getSomeField(); @@ -313,17 +363,17 @@ void test() { import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; - + @ExtendWith(MockitoExtension.class) class MyTest { @Mock MyObject myObject; - + String expected = "expected"; - + void test() { when(myObject.getSomeField()).thenReturn(expected); assertEquals(expected, myObject.getSomeField()); @@ -348,19 +398,19 @@ public Object getSomeField() { """ ), java( - """ + """ import mockit.Expectations; import mockit.Mocked; import mockit.integration.junit5.JMockitExtension; import org.junit.jupiter.api.extension.ExtendWith; - + import static org.junit.jupiter.api.Assertions.assertNotNull; - + @ExtendWith(JMockitExtension.class) class MyTest { @Mocked MyObject myObject; - + void test() { new Expectations() {{ myObject.getSomeField(); @@ -374,7 +424,7 @@ void test() { import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - + import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.Mockito.when; @@ -468,7 +518,7 @@ public String getSomeField() { import mockit.Mocked; import mockit.integration.junit5.JMockitExtension; import org.junit.jupiter.api.extension.ExtendWith; - + import static org.junit.jupiter.api.Assertions.assertEquals; @ExtendWith(JMockitExtension.class) @@ -517,7 +567,7 @@ void whenClassArgumentMatcher() { java( """ import java.util.List; - + class MyObject { public String getSomeField(List input) { return "X"; @@ -529,22 +579,22 @@ public String getSomeOtherField(Object input) { """ ), java( - """ + """ import java.util.ArrayList; import java.util.List; - + import mockit.Expectations; import mockit.Mocked; import mockit.integration.junit5.JMockitExtension; import org.junit.jupiter.api.extension.ExtendWith; - + import static org.junit.jupiter.api.Assertions.assertNull; - + @ExtendWith(JMockitExtension.class) class MyTest { @Mocked MyObject myObject; - + void test() { new Expectations() {{ myObject.getSomeField((List) any); @@ -560,19 +610,19 @@ void test() { """ import java.util.ArrayList; import java.util.List; - + import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - + import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.*; - + @ExtendWith(MockitoExtension.class) class MyTest { @Mock MyObject myObject; - + void test() { when(myObject.getSomeField(anyList())).thenReturn(null); when(myObject.getSomeOtherField(any(Object.class))).thenReturn(null); @@ -585,6 +635,73 @@ void test() { ); } + @Test + void whenNoArguments() { + //language=java + rewriteRun( + java( + """ + import java.util.List; + + class MyObject { + public String getSomeField() { + return "X"; + } + } + """ + ), + java( + """ + import java.util.ArrayList; + import java.util.List; + + import mockit.Expectations; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + import static org.junit.jupiter.api.Assertions.assertNull; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + MyObject myObject; + + void test() { + new Expectations() {{ + myObject.getSomeField(); + result = null; + }}; + assertNull(myObject.getSomeField()); + } + } + """, + """ + import java.util.ArrayList; + import java.util.List; + + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.junit.jupiter.api.Assertions.assertNull; + import static org.mockito.Mockito.when; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + MyObject myObject; + + void test() { + when(myObject.getSomeField()).thenReturn(null); + assertNull(myObject.getSomeField()); + } + } + """ + ) + ); + } + @Test void whenMixedArgumentMatcher() { //language=java @@ -592,7 +709,7 @@ void whenMixedArgumentMatcher() { java( """ import java.util.List; - + class MyObject { public String getSomeField(String s, String s2, String s3, long l1) { return "X"; @@ -601,22 +718,22 @@ public String getSomeField(String s, String s2, String s3, long l1) { """ ), java( - """ + """ import java.util.ArrayList; import java.util.List; - + import mockit.Expectations; import mockit.Mocked; import mockit.integration.junit5.JMockitExtension; import org.junit.jupiter.api.extension.ExtendWith; - + import static org.junit.jupiter.api.Assertions.assertNull; - + @ExtendWith(JMockitExtension.class) class MyTest { @Mocked MyObject myObject; - + void test() { String bazz = "bazz"; new Expectations() {{ @@ -630,19 +747,19 @@ void test() { """ import java.util.ArrayList; import java.util.List; - + import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - + import static org.junit.jupiter.api.Assertions.assertNull; import static org.mockito.Mockito.*; - + @ExtendWith(MockitoExtension.class) class MyTest { @Mock MyObject myObject; - + void test() { String bazz = "bazz"; when(myObject.getSomeField(eq("foo"), anyString(), eq(bazz), eq(10L))).thenReturn(null); @@ -661,7 +778,7 @@ void whenSetupStatements() { java( """ class MyObject { - + public String getSomeField(String s) { return "X"; } @@ -672,31 +789,31 @@ public String getString() { """ ), java( - """ + """ import mockit.Expectations; import mockit.Mocked; import mockit.integration.junit5.JMockitExtension; import org.junit.jupiter.api.extension.ExtendWith; - + import static org.junit.jupiter.api.Assertions.assertEquals; - + @ExtendWith(JMockitExtension.class) class MyTest { @Mocked MyObject myObject; - + void test() { String a = "a"; String s = "s"; - + new Expectations() {{ myObject.getSomeField(anyString); result = s; - + myObject.getString(); result = a; }}; - + assertEquals("s", myObject.getSomeField("foo")); assertEquals("a", myObject.getString()); } @@ -706,7 +823,7 @@ void test() { import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.when; @@ -719,11 +836,9 @@ class MyTest { void test() { String a = "a"; String s = "s"; - when(myObject.getSomeField(anyString())).thenReturn(s); - when(myObject.getString()).thenReturn(a); - + assertEquals("s", myObject.getSomeField("foo")); assertEquals("a", myObject.getString()); } @@ -747,19 +862,19 @@ public String getSomeField(String s) { """ ), java( - """ + """ import mockit.Expectations; import mockit.Mocked; import mockit.integration.junit5.JMockitExtension; import org.junit.jupiter.api.extension.ExtendWith; - + import static org.junit.jupiter.api.Assertions.assertEquals; - + @ExtendWith(JMockitExtension.class) class MyTest { @Mocked MyObject myObject; - + void test() { String a = "a"; new Expectations() {{ @@ -768,7 +883,7 @@ void test() { String b = "b"; result = s; }}; - + assertEquals("s", myObject.getSomeField("foo")); } } @@ -777,7 +892,7 @@ void test() { import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.when; @@ -792,7 +907,7 @@ void test() { String s = "s"; String b = "b"; when(myObject.getSomeField(anyString())).thenReturn(s); - + assertEquals("s", myObject.getSomeField("foo")); } } @@ -806,12 +921,63 @@ void whenTimes() { //language=java rewriteRun( java( - """ + """ + import mockit.Expectations; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + new Expectations() {{ + myObject.wait(anyLong, anyInt); + times = 3; + }}; + myObject.wait(10L, 10); + myObject.wait(10L, 10); + myObject.wait(10L, 10); + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.*; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(10L, 10); + myObject.wait(10L, 10); + myObject.wait(10L, 10); + verify(myObject, times(3)).wait(anyLong(), anyInt()); + } + } + """ + ) + ); + } + + @Test + void whenMinTimes() { + //language=java + rewriteRun( + java( + """ import mockit.Expectations; import mockit.Mocked; import mockit.integration.junit5.JMockitExtension; import org.junit.jupiter.api.extension.ExtendWith; - + @ExtendWith(JMockitExtension.class) class MyTest { @Mocked @@ -820,9 +986,55 @@ class MyTest { void test() { new Expectations() {{ myObject.wait(anyLong, anyInt); - times = 2; + minTimes = 2; }}; myObject.wait(10L, 10); + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.*; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(10L, 10); + verify(myObject, atLeast(2)).wait(anyLong(), anyInt()); + } + } + """ + ) + ); + } + + @Test + void whenMaxTimes() { + //language=java + rewriteRun( + java( + """ + import mockit.Expectations; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + new Expectations() {{ + myObject.wait(anyLong, anyInt); + maxTimes = 5; + }}; myObject.wait(10L, 10); } } @@ -831,9 +1043,9 @@ void test() { import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; - + import static org.mockito.Mockito.*; - + @ExtendWith(MockitoExtension.class) class MyTest { @Mock @@ -841,8 +1053,56 @@ class MyTest { void test() { myObject.wait(10L, 10); + verify(myObject, atMost(5)).wait(anyLong(), anyInt()); + } + } + """ + ) + ); + } + + @Test + void whenMinTimesMaxTimes() { + //language=java + rewriteRun( + java( + """ + import mockit.Expectations; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + new Expectations() {{ + myObject.wait(anyLong, anyInt); + minTimes = 1; + maxTimes = 3; + }}; myObject.wait(10L, 10); - verify(myObject, times(2)).wait(anyLong(), anyInt()); + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.*; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(10L, 10); + verify(myObject, atLeast(1)).wait(anyLong(), anyInt()); + verify(myObject, atMost(3)).wait(anyLong(), anyInt()); } } """ @@ -869,14 +1129,14 @@ public String getSomeField() { import mockit.Tested; import mockit.integration.junit5.JMockitExtension; import org.junit.jupiter.api.extension.ExtendWith; - + import static org.junit.jupiter.api.Assertions.assertEquals; - + @ExtendWith(JMockitExtension.class) class MyTest { @Tested MyObject myObject; - + void test() { new Expectations(myObject) {{ myObject.getSomeField(); @@ -890,15 +1150,15 @@ void test() { import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.InjectMocks; import org.mockito.junit.jupiter.MockitoExtension; - + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.mockito.Mockito.when; - + @ExtendWith(MockitoExtension.class) class MyTest { @InjectMocks MyObject myObject; - + void test() { when(myObject.getSomeField()).thenReturn("foo"); assertEquals("foo", myObject.getSomeField()); @@ -935,18 +1195,18 @@ public void doSomething() {} import mockit.Mocked; import mockit.integration.junit5.JMockitExtension; import org.junit.jupiter.api.extension.ExtendWith; - + import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNull; - + @ExtendWith(JMockitExtension.class) class MyTest { @Mocked Object myObject; - + @Mocked MyObject myOtherObject; - + void test() { new Expectations() {{ myObject.hashCode(); @@ -989,6 +1249,133 @@ void test() { assertNull(myOtherObject.getSomeObjectField()); myObject.wait(10L, 10); assertEquals("foo", myOtherObject.getSomeStringField("bar", 10L)); + verify(myObject).wait(anyLong(), anyInt()); + } + } + """ + ) + ); + } + + @Test + void whenMultipleExpectations() { + //language=java + rewriteRun( + java( + """ + class MyObject { + public String getSomeStringField() { + return "X"; + } + } + """ + ), + java( + """ + import mockit.Expectations; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + import static org.junit.jupiter.api.Assertions.assertEquals; + import static org.junit.jupiter.api.Assertions.assertNull; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + MyObject myObject; + + void test() { + new Expectations() {{ + myObject.getSomeStringField(); + result = "a"; + }}; + assertEquals("a", myObject.getSomeStringField()); + new Expectations() {{ + myObject.getSomeStringField(); + result = "b"; + }}; + assertEquals("b", myObject.getSomeStringField()); + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.junit.jupiter.api.Assertions.assertEquals; + import static org.junit.jupiter.api.Assertions.assertNull; + import static org.mockito.Mockito.when; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + MyObject myObject; + + void test() { + when(myObject.getSomeStringField()).thenReturn("a"); + assertEquals("a", myObject.getSomeStringField()); + when(myObject.getSomeStringField()).thenReturn("b"); + assertEquals("b", myObject.getSomeStringField()); + } + } + """ + ) + ); + } + + @Test + void whenMultipleExpectationsNoResults() { + //language=java + rewriteRun( + java( + """ + import mockit.Expectations; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + import static org.junit.jupiter.api.Assertions.assertEquals; + import static org.junit.jupiter.api.Assertions.assertNull; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + new Expectations() {{ + myObject.wait(anyLong); + }}; + myObject.wait(1L); + new Expectations() {{ + myObject.wait(); + }}; + myObject.wait(); + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.junit.jupiter.api.Assertions.assertEquals; + import static org.junit.jupiter.api.Assertions.assertNull; + import static org.mockito.Mockito.anyLong; + import static org.mockito.Mockito.verify; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(1L); + myObject.wait(); + verify(myObject).wait(anyLong()); + verify(myObject).wait(); } } """ diff --git a/src/test/java/org/openrewrite/java/testing/jmockit/JMockitVerificationsToMockitoTest.java b/src/test/java/org/openrewrite/java/testing/jmockit/JMockitVerificationsToMockitoTest.java new file mode 100644 index 000000000..23022dcb5 --- /dev/null +++ b/src/test/java/org/openrewrite/java/testing/jmockit/JMockitVerificationsToMockitoTest.java @@ -0,0 +1,740 @@ +/* + * 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.jmockit; + +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 org.openrewrite.test.TypeValidation; + +import static org.openrewrite.java.Assertions.java; + +class JMockitVerificationsToMockitoTest implements RewriteTest { + @Override + public void defaults(RecipeSpec spec) { + spec + .parser(JavaParser.fromJavaVersion() + .logCompilationWarningsAndErrors(true) + .classpathFromResources(new InMemoryExecutionContext(), + "junit-jupiter-api-5.9", + "jmockit-1.49", + "mockito-core-3.12", + "mockito-junit-jupiter-3.12" + )) + .recipeFromResource( + "/META-INF/rewrite/jmockit.yml", + "org.openrewrite.java.testing.jmockit.JMockitToMockito" + ); + } + + @DocumentExample + @Test + void whenNoTimes() { + //language=java + rewriteRun( + java( + """ + import mockit.Verifications; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + myObject.wait(10L, 10); + new Verifications() {{ + myObject.wait(anyLong, anyInt); + }}; + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.*; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(10L, 10); + verify(myObject).wait(anyLong(), anyInt()); + } + } + """ + ) + ); + } + + @DocumentExample + @Test + void whenNoTimesNoArgs() { + //language=java + rewriteRun( + java( + """ + import mockit.Verifications; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + myObject.wait(10L, 10); + new Verifications() {{ + myObject.wait(); + }}; + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.verify; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(10L, 10); + verify(myObject).wait(); + } + } + """ + ) + ); + } + + + @Test + void whenTimesNoArgs() { + //language=java + rewriteRun( + java( + """ + import java.util.ArrayList; + import java.util.List; + + import mockit.Verifications; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + myObject.wait(); + myObject.wait(); + new Verifications() {{ + myObject.wait(); + times = 2; + }}; + } + } + """, + """ + import java.util.ArrayList; + import java.util.List; + + import static org.mockito.Mockito.times; + import static org.mockito.Mockito.verify; + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(); + myObject.wait(); + verify(myObject, times(2)).wait(); + } + } + """ + ) + ); + } + + @Test + void whenClassArgumentMatcher() { + //language=java + rewriteRun( + spec -> spec.afterTypeValidationOptions(TypeValidation.builder().methodInvocations(false).build()), + java( + """ + import java.util.List; + + class MyObject { + public String getSomeField(List input) { + return "X"; + } + public String getSomeOtherField(Object input) { + return "Y"; + } + } + """ + ), + java( + """ + import java.util.ArrayList; + import java.util.List; + + import mockit.Mocked; + import mockit.Verifications; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + MyObject myObject; + + void test() { + myObject.getSomeField(new ArrayList<>()); + myObject.getSomeOtherField(new Object()); + new Verifications() {{ + myObject.getSomeField((List) any); + myObject.getSomeOtherField((Object) any); + }}; + } + } + """, + """ + import java.util.ArrayList; + import java.util.List; + + import static org.mockito.Mockito.*; + + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + MyObject myObject; + + void test() { + myObject.getSomeField(new ArrayList<>()); + myObject.getSomeOtherField(new Object()); + verify(myObject).getSomeField(anyList()); + verify(myObject).getSomeOtherField(any(Object.class)); + } + } + """ + ) + ); + } + + + @Test + void whenMixedArgumentMatcher() { + //language=java + rewriteRun( + java( + """ + import java.util.ArrayList; + import java.util.List; + + import mockit.Verifications; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + myObject.wait(10L, 10); + new Verifications() {{ + myObject.wait(anyLong, 10); + }}; + } + } + """, + """ + import java.util.ArrayList; + import java.util.List; + + import static org.mockito.Mockito.*; + + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(10L, 10); + verify(myObject).wait(anyLong(), eq(10)); + } + } + """ + ) + ); + } + + @Test + void whenSetupStatements() { + //language=java + rewriteRun( + java( + """ + import mockit.Verifications; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + String a = "a"; + String s = "s"; + myObject.wait(1L); + myObject.wait(); + new Verifications() {{ + myObject.wait(anyLong); + myObject.wait(); + }}; + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.anyLong; + import static org.mockito.Mockito.verify; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + String a = "a"; + String s = "s"; + myObject.wait(1L); + myObject.wait(); + verify(myObject).wait(anyLong()); + verify(myObject).wait(); + } + } + """ + ) + ); + } + + @Test + void whenSetupStatements2() { + //language=java + rewriteRun( + java( + """ + import mockit.Verifications; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + String a = "a"; + myObject.wait(1L); + new Verifications() {{ + myObject.wait(anyLong); + }}; + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.anyLong; + import static org.mockito.Mockito.verify; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + String a = "a"; + myObject.wait(1L); + verify(myObject).wait(anyLong()); + } + } + """ + ) + ); + } + + @Test + void whenTimes() { + //language=java + rewriteRun( + java( + """ + import mockit.Verifications; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + myObject.wait(10L, 10); + myObject.wait(10L, 10); + myObject.wait(10L, 10); + new Verifications() {{ + myObject.wait(anyLong, anyInt); + times = 3; + }}; + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.*; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(10L, 10); + myObject.wait(10L, 10); + myObject.wait(10L, 10); + verify(myObject, times(3)).wait(anyLong(), anyInt()); + } + } + """ + ) + ); + } + + @Test + void whenMinTimes() { + //language=java + rewriteRun( + java( + """ + import mockit.Verifications; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + myObject.wait(10L, 10); + new Verifications() {{ + myObject.wait(anyLong, anyInt); + minTimes = 2; + }}; + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.*; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(10L, 10); + verify(myObject, atLeast(2)).wait(anyLong(), anyInt()); + } + } + """ + ) + ); + } + + @Test + void whenMaxTimes() { + //language=java + rewriteRun( + java( + """ + import mockit.Verifications; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + myObject.wait(10L, 10); + new Verifications() {{ + myObject.wait(anyLong, anyInt); + maxTimes = 5; + }}; + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.*; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(10L, 10); + verify(myObject, atMost(5)).wait(anyLong(), anyInt()); + } + } + """ + ) + ); + } + + @Test + void whenMinTimesMaxTimes() { + //language=java + rewriteRun( + java( + """ + import mockit.Verifications; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + myObject.wait(10L, 10); + new Verifications() {{ + myObject.wait(anyLong, anyInt); + minTimes = 1; + maxTimes = 3; + }}; + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.*; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(10L, 10); + verify(myObject, atLeast(1)).wait(anyLong(), anyInt()); + verify(myObject, atMost(3)).wait(anyLong(), anyInt()); + } + } + """ + ) + ); + } + + @Test + void whenMultipleStatements() { + //language=java + rewriteRun( + java( + """ + import mockit.Verifications; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + @Mocked + Object myOtherObject; + + void test() { + myObject.hashCode(); + myOtherObject.wait(); + myObject.wait(10L, 10); + myOtherObject.wait(10L); + new Verifications() {{ + myObject.hashCode(); + myOtherObject.wait(); + myObject.wait(anyLong, anyInt); + myOtherObject.wait(anyLong); + }}; + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.*; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + @Mock + Object myOtherObject; + + void test() { + myObject.hashCode(); + myOtherObject.wait(); + myObject.wait(10L, 10); + myOtherObject.wait(10L); + verify(myObject).hashCode(); + verify(myOtherObject).wait(); + verify(myObject).wait(anyLong(), anyInt()); + verify(myOtherObject).wait(anyLong()); + } + } + """ + ) + ); + } + + @Test + void whenMultipleVerificationsAndMultipleStatements() { + //language=java + rewriteRun( + java( + """ + import mockit.Verifications; + import mockit.Mocked; + import mockit.integration.junit5.JMockitExtension; + import org.junit.jupiter.api.extension.ExtendWith; + + @ExtendWith(JMockitExtension.class) + class MyTest { + @Mocked + Object myObject; + + void test() { + myObject.wait(); + new Verifications() {{ + + myObject.wait(); + + myObject.wait(anyLong, anyInt); + }}; + myObject.wait(1L); + myObject.wait(2L); + new Verifications() {{ + myObject.wait(anyLong); + times = 2; + }}; + } + } + """, + """ + import org.junit.jupiter.api.extension.ExtendWith; + import org.mockito.Mock; + import org.mockito.junit.jupiter.MockitoExtension; + + import static org.mockito.Mockito.*; + + @ExtendWith(MockitoExtension.class) + class MyTest { + @Mock + Object myObject; + + void test() { + myObject.wait(); + verify(myObject).wait(); + verify(myObject).wait(anyLong(), anyInt()); + myObject.wait(1L); + myObject.wait(2L); + verify(myObject, times(2)).wait(anyLong()); + } + } + """ + ) + ); + } +} diff --git a/src/test/java/org/openrewrite/java/testing/junit5/AddParameterizedTestAnnotationTest.java b/src/test/java/org/openrewrite/java/testing/junit5/AddParameterizedTestAnnotationTest.java index 5b2e87923..5103fea4d 100644 --- a/src/test/java/org/openrewrite/java/testing/junit5/AddParameterizedTestAnnotationTest.java +++ b/src/test/java/org/openrewrite/java/testing/junit5/AddParameterizedTestAnnotationTest.java @@ -20,10 +20,12 @@ import org.openrewrite.InMemoryExecutionContext; import org.openrewrite.Issue; import org.openrewrite.java.JavaParser; +import org.openrewrite.kotlin.KotlinParser; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.kotlin.Assertions.kotlin; class AddParameterizedTestAnnotationTest implements RewriteTest { @Override @@ -31,6 +33,8 @@ public void defaults(RecipeSpec spec) { spec .parser(JavaParser.fromJavaVersion() .classpathFromResources(new InMemoryExecutionContext(), "junit-jupiter-api-5.9", "junit-jupiter-params-5.9")) + .parser(KotlinParser.builder() + .classpathFromResources(new InMemoryExecutionContext(), "junit-jupiter-api-5.9", "junit-jupiter-params-5.9")) .recipe(new AddParameterizedTestAnnotation()); } @@ -45,7 +49,7 @@ void replaceTestWithParameterizedTest() { import org.junit.jupiter.api.Test; import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; - + class NumbersTest { @Test @ValueSource(ints = {1, 3, 5, -3, 15, Integer.MAX_VALUE}) @@ -58,7 +62,7 @@ void testIsOdd(int number) { import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.*; - + class NumbersTest { @ParameterizedTest @ValueSource(ints = {1, 3, 5, -3, 15, Integer.MAX_VALUE}) @@ -67,6 +71,35 @@ void testIsOdd(int number) { } } """ + ), + //language=kotlin + kotlin( + """ + import org.junit.jupiter.api.Test + import org.junit.jupiter.params.provider.ValueSource + import org.junit.jupiter.api.Assertions.assertTrue + + class NumbersTest { + @Test + @ValueSource(ints = [1, 3, 5, -3, 15, Int.MAX_VALUE]) + fun testIsOdd(number: Int) { + assertTrue(number % 2 != 0) + } + } + """, + """ + import org.junit.jupiter.params.ParameterizedTest + import org.junit.jupiter.params.provider.ValueSource + import org.junit.jupiter.api.Assertions.assertTrue + + class NumbersTest { + @ParameterizedTest + @ValueSource(ints = [1, 3, 5, -3, 15, Int.MAX_VALUE]) + fun testIsOdd(number: Int) { + assertTrue(number % 2 != 0) + } + } + """ ) ); } diff --git a/src/test/java/org/openrewrite/java/testing/junit5/AssertToAssertionsTest.java b/src/test/java/org/openrewrite/java/testing/junit5/AssertToAssertionsTest.java index aba15128f..f93144656 100644 --- a/src/test/java/org/openrewrite/java/testing/junit5/AssertToAssertionsTest.java +++ b/src/test/java/org/openrewrite/java/testing/junit5/AssertToAssertionsTest.java @@ -23,6 +23,7 @@ import org.openrewrite.java.JavaParser; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.SourceSpec; import org.openrewrite.test.TypeValidation; import static org.openrewrite.java.Assertions.java; @@ -55,7 +56,7 @@ String getField() { java( """ import static org.junit.Assert.assertEquals; - + class MyTest { void foo() { Entity entity = new Entity(); @@ -66,7 +67,7 @@ void foo() { """, """ import static org.junit.jupiter.api.Assertions.assertEquals; - + class MyTest { void foo() { Entity entity = new Entity(); @@ -147,7 +148,7 @@ public void test() { """, """ import org.junit.Test; - + import static org.junit.jupiter.api.Assertions.assertFalse; public class MyTest { @@ -169,9 +170,9 @@ void assertWithoutMessage() { java( """ import org.junit.Assert; - + class MyTest { - + void foo() { Assert.assertEquals(1, 2); Assert.assertArrayEquals(new int[]{}, new int[]{}); @@ -193,9 +194,9 @@ void foo() { """, """ import org.junit.jupiter.api.Assertions; - + class MyTest { - + void foo() { Assertions.assertEquals(1, 2); Assertions.assertArrayEquals(new int[]{}, new int[]{}); @@ -226,9 +227,9 @@ void staticAssertWithoutMessage() { java( """ import static org.junit.Assert.*; - + class MyTest { - + void foo() { assertEquals(1, 2); assertArrayEquals(new int[]{}, new int[]{}); @@ -243,9 +244,9 @@ void foo() { """, """ import static org.junit.jupiter.api.Assertions.*; - + class MyTest { - + void foo() { assertEquals(1, 2); assertArrayEquals(new int[]{}, new int[]{}); @@ -269,9 +270,9 @@ void assertWithMessage() { java( """ import org.junit.Assert; - + class MyTest { - + void foo() { Assert.assertEquals("One is one", 1, 1); Assert.assertArrayEquals("Empty is empty", new int[]{}, new int[]{}); @@ -290,9 +291,9 @@ void foo() { """, """ import org.junit.jupiter.api.Assertions; - + class MyTest { - + void foo() { Assertions.assertEquals(1, 1, "One is one"); Assertions.assertArrayEquals(new int[]{}, new int[]{}, "Empty is empty"); @@ -326,7 +327,7 @@ void staticallyImportAssertions() { java( """ import org.junit.Assert; - + class Test { void test() { Assert.assertEquals("One is one", 1, 1); @@ -335,7 +336,7 @@ void test() { """, """ import static org.junit.jupiter.api.Assertions.assertEquals; - + class Test { void test() { assertEquals(1, 1, "One is one"); @@ -354,7 +355,7 @@ void swapAssertTrueArgumentsWhenMessageIsBinaryExpression() { java( """ import org.junit.Assert; - + class Test { void test() { Assert.assertTrue("one" + "one", true); @@ -363,7 +364,7 @@ void test() { """, """ import org.junit.jupiter.api.Assertions; - + class Test { void test() { Assertions.assertTrue(true, "one" + "one"); @@ -391,7 +392,7 @@ void testNestedPartitionStepStepReference() { """, """ import static org.junit.jupiter.api.Assertions.assertNotNull; - + class MyTest { Long l = 1L; void testNestedPartitionStepStepReference() { @@ -429,7 +430,7 @@ void test(Runnable run) { """, """ import static org.junit.jupiter.api.Assertions.assertThrows; - + class Test { void test(Runnable run) { assertThrows( @@ -457,7 +458,7 @@ void missingTypeInfo() { java( """ import static org.junit.Assert.*; - + class MyTest { void test() { assertNotNull(UnknownType.unknownMethod()); @@ -466,7 +467,7 @@ void test() { """, """ import static org.junit.jupiter.api.Assertions.*; - + class MyTest { void test() { assertNotNull(UnknownType.unknownMethod()); @@ -487,7 +488,7 @@ void migratesAssertStatementsWithMissingTypeInfo() { java( """ import static org.junit.Assert.assertNotNull; - + class MyTest { void test() { assertNotNull(UnknownType.unknownMethod()); @@ -497,7 +498,7 @@ void test() { """ import static org.junit.Assert.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotNull; - + class MyTest { void test() { assertNotNull(UnknownType.unknownMethod()); @@ -507,4 +508,50 @@ void test() { ) ); } + +@Test +@Issue("https://github.com/openrewrite/rewrite-testing-frameworks/issues/515") +void verifyClassExtendsAssertMethodArgumentsOrderRetained() { + //language=java + rewriteRun( + java( + """ + package foo; + import org.junit.Assert; + public class Verify extends Assert { + public static void assertContains(String expected, String actual) { + } + } + """, + SourceSpec::skip + ), + java( + """ + import foo.Verify; + import org.junit.Assert; + import java.util.List; + + class A { + void test(String message, String expected, String actual) { + Verify.assertContains(expected, actual); + Assert.assertEquals(message, expected, actual); + } + } + """, + """ + import foo.Verify; + import org.junit.jupiter.api.Assertions; + + import java.util.List; + + class A { + void test(String message, String expected, String actual) { + Verify.assertContains(expected, actual); + Assertions.assertEquals(expected, actual, message); + } + } + """ + ) + ); +} } diff --git a/src/test/java/org/openrewrite/java/testing/junit5/CleanupJUnitImportsTest.java b/src/test/java/org/openrewrite/java/testing/junit5/CleanupJUnitImportsTest.java index f59a1cbea..30ec0e109 100644 --- a/src/test/java/org/openrewrite/java/testing/junit5/CleanupJUnitImportsTest.java +++ b/src/test/java/org/openrewrite/java/testing/junit5/CleanupJUnitImportsTest.java @@ -19,10 +19,12 @@ import org.openrewrite.DocumentExample; import org.openrewrite.InMemoryExecutionContext; import org.openrewrite.java.JavaParser; +import org.openrewrite.kotlin.KotlinParser; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.kotlin.Assertions.kotlin; class CleanupJUnitImportsTest implements RewriteTest { @@ -31,14 +33,16 @@ public void defaults(RecipeSpec spec) { spec .parser(JavaParser.fromJavaVersion() .classpathFromResources(new InMemoryExecutionContext(), "junit-4.13")) + .parser(KotlinParser.builder() + .classpathFromResources(new InMemoryExecutionContext(), "junit-4.13")) .recipe(new CleanupJUnitImports()); } @DocumentExample @Test void removesUnusedImport() { - //language=java rewriteRun( + //language=java java( """ import org.junit.Test; @@ -48,14 +52,25 @@ public class MyTest {} """ public class MyTest {} """ + ), + //language=kotlin + kotlin( + """ + import org.junit.Test + + class MyTest {} + """, + """ + class MyTest {} + """ ) ); } @Test void leavesOtherImportsAlone() { - //language=java rewriteRun( + //language=java java( """ import java.util.Arrays; @@ -65,14 +80,25 @@ void leavesOtherImportsAlone() { public class MyTest { } """ + ), + //language=kotlin + kotlin( + """ + import java.util.Arrays + import java.util.Collections + import java.util.HashSet + + class MyTest { + } + """ ) ); } @Test void leavesUsedJUnitImportAlone() { - //language=java rewriteRun( + //language=java java( """ import org.junit.Test; @@ -82,7 +108,19 @@ public class MyTest { public void foo() {} } """ + ), + //language=kotlin + kotlin( + """ + import org.junit.Test + + class MyTest { + @Test + fun foo() {} + } + """ ) ); + } } diff --git a/src/test/java/org/openrewrite/java/testing/junit5/JUnit5MigrationTest.java b/src/test/java/org/openrewrite/java/testing/junit5/JUnit5MigrationTest.java index 022b61840..17478e64c 100644 --- a/src/test/java/org/openrewrite/java/testing/junit5/JUnit5MigrationTest.java +++ b/src/test/java/org/openrewrite/java/testing/junit5/JUnit5MigrationTest.java @@ -16,15 +16,16 @@ package org.openrewrite.java.testing.junit5; import org.junit.jupiter.api.Test; +import org.openrewrite.DocumentExample; import org.openrewrite.InMemoryExecutionContext; import org.openrewrite.Issue; +import org.openrewrite.Tree; import org.openrewrite.config.Environment; import org.openrewrite.java.JavaParser; +import org.openrewrite.marker.BuildTool; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; -import java.util.List; -import java.util.regex.MatchResult; import java.util.regex.Pattern; import static org.assertj.core.api.Assertions.assertThat; @@ -74,6 +75,7 @@ void method() { ); } + @DocumentExample @Test @Issue("https://github.com/openrewrite/rewrite-testing-frameworks/issues/145") void assertThatReceiver() { @@ -145,12 +147,7 @@ void upgradeMavenPluginVersions() { """, spec -> spec.after(actual -> { - List list = Pattern.compile("(.*)") - .matcher(actual).results().skip(1).toList(); - assertThat(list) - .hasSize(2) - .extracting(mr -> mr.group(1)) - .allMatch(m -> m.startsWith("3.")); + assertThat(Pattern.compile("3\\.(.*)").matcher(actual).results().toList()).hasSize(2); return actual; }) ) @@ -316,7 +313,7 @@ public void test() { import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; - + public class A extends AbstractTest { @BeforeEach public void before() { @@ -341,38 +338,82 @@ void noJunitDependencyIfApiAlreadyPresent() { spec -> spec.beforeRecipe(withToolingApi()), //language=groovy buildGradle( - """ - plugins { - id 'java-library' - } - repositories { - mavenCentral() - } - dependencies { - testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2' - } - tasks.withType(Test).configureEach { - useJUnitPlatform() - } - """), + """ + plugins { + id 'java-library' + } + repositories { + mavenCentral() + } + dependencies { + testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2' + } + tasks.withType(Test).configureEach { + useJUnitPlatform() + } + """), //language=xml pomXml( - """ - - 4.0.0 - dev.ted - testcontainer-migrate - 0.0.1 - - - org.junit.jupiter - junit-jupiter-api - 5.7.2 - test - - - - """) + """ + + 4.0.0 + dev.ted + testcontainer-migrate + 0.0.1 + + + org.junit.jupiter + junit-jupiter-api + 5.7.2 + test + + + + """) + ); + } + + @Test + void bumpSurefireOnOlderMavenVersions() { + rewriteRun( + spec -> spec.recipeFromResource("/META-INF/rewrite/junit5.yml", "org.openrewrite.java.testing.junit5.UpgradeSurefirePlugin"), + pomXml( + //language=xml + """ + + 4.0.0 + dev.ted + testcontainer-migrate + 0.0.1 + + """, + //language=xml + """ + + 4.0.0 + dev.ted + testcontainer-migrate + 0.0.1 + + + + org.apache.maven.plugins + maven-surefire-plugin + 3.2.5 + + + org.junit.platform + junit-platform-surefire-provider + 1.1.0 + + + + + + + """, + spec -> spec.markers(new BuildTool(Tree.randomId(), BuildTool.Type.Maven, "3.5.4")) + ) ); } } diff --git a/src/test/java/org/openrewrite/java/testing/junit5/UpdateBeforeAfterAnnotationsTest.java b/src/test/java/org/openrewrite/java/testing/junit5/UpdateBeforeAfterAnnotationsTest.java index 47d723d7a..2c71b6107 100644 --- a/src/test/java/org/openrewrite/java/testing/junit5/UpdateBeforeAfterAnnotationsTest.java +++ b/src/test/java/org/openrewrite/java/testing/junit5/UpdateBeforeAfterAnnotationsTest.java @@ -21,10 +21,12 @@ import org.openrewrite.InMemoryExecutionContext; import org.openrewrite.Issue; import org.openrewrite.java.JavaParser; +import org.openrewrite.kotlin.KotlinParser; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.kotlin.Assertions.kotlin; @SuppressWarnings("JUnitMalformedDeclaration") class UpdateBeforeAfterAnnotationsTest implements RewriteTest { @@ -34,6 +36,8 @@ public void defaults(RecipeSpec spec) { spec .parser(JavaParser.fromJavaVersion() .classpathFromResources(new InMemoryExecutionContext(), "junit-4.13")) + .parser(KotlinParser.builder() + .classpathFromResources(new InMemoryExecutionContext(), "junit-4.13")) .recipe(new UpdateBeforeAfterAnnotations()); } @@ -45,9 +49,8 @@ void beforeToBeforeEach() { java( """ import org.junit.Before; - + class Test { - @Before void before() { } @@ -55,14 +58,36 @@ void before() { """, """ import org.junit.jupiter.api.BeforeEach; - + class Test { - @BeforeEach void before() { } } """ + ), + //language=kotlin + kotlin( + """ + import org.junit.Before + + class Test { + + @Before + fun before() { + } + } + """, + """ + import org.junit.jupiter.api.BeforeEach + + class Test { + + @BeforeEach + fun before() { + } + } + """ ) ); } @@ -185,8 +210,8 @@ void before() { } @Test - @Disabled("Issue #59") - void beforeMethodOverridesPublicAbstract() { + @Issue("https://github.com/openrewrite/rewrite-testing-frameworks/issues/59") + void retainPublicModifierOnOverriddenMethod() { //language=java rewriteRun( diff --git a/src/test/java/org/openrewrite/java/testing/junit5/UseXMLUnitLegacyTest.java b/src/test/java/org/openrewrite/java/testing/junit5/UseXMLUnitLegacyTest.java index 1e59df35a..902f8fa99 100644 --- a/src/test/java/org/openrewrite/java/testing/junit5/UseXMLUnitLegacyTest.java +++ b/src/test/java/org/openrewrite/java/testing/junit5/UseXMLUnitLegacyTest.java @@ -64,7 +64,7 @@ void shouldMigrateMavenDependency() { org.xmlunit xmlunit-legacy - 2.9.1 + 2.10.0 diff --git a/src/test/java/org/openrewrite/java/testing/mockito/CleanupMockitoImportsTest.java b/src/test/java/org/openrewrite/java/testing/mockito/CleanupMockitoImportsTest.java index 94ad05d85..cf36f76cb 100755 --- a/src/test/java/org/openrewrite/java/testing/mockito/CleanupMockitoImportsTest.java +++ b/src/test/java/org/openrewrite/java/testing/mockito/CleanupMockitoImportsTest.java @@ -19,10 +19,13 @@ import org.openrewrite.DocumentExample; import org.openrewrite.InMemoryExecutionContext; import org.openrewrite.java.JavaParser; +import org.openrewrite.kotlin.KotlinParser; import org.openrewrite.test.RecipeSpec; import org.openrewrite.test.RewriteTest; +import org.openrewrite.test.TypeValidation; import static org.openrewrite.java.Assertions.java; +import static org.openrewrite.kotlin.Assertions.kotlin; class CleanupMockitoImportsTest implements RewriteTest { @@ -31,6 +34,7 @@ public void defaults(RecipeSpec spec) { spec .parser(JavaParser.fromJavaVersion() .classpathFromResources(new InMemoryExecutionContext(), "mockito-all-1.10")) + .parser(KotlinParser.builder().classpath("mockito-core", "mockito-kotlin")) .recipe(new CleanupMockitoImports()); } @@ -43,12 +47,12 @@ void removesUnusedMockitoImport() { """ import org.mockito.Mock; import java.util.Arrays; - + public class MyTest {} """, """ import java.util.Arrays; - + public class MyTest {} """ ) @@ -65,7 +69,7 @@ void leavesOtherImportsAlone() { import java.util.Collections; import java.util.HashSet; import java.util.List; - + public class MyTest {} """ ) @@ -85,7 +89,7 @@ void doNotRemoveImportsPossiblyAssociatedWithAnUntypedMockitoMethod() { class MyObjectTest { MyObject myObject; MyMockClass myMock; - + void test() { when(myObject.getSomeField()).thenReturn("testValue"); given(myObject.getSomeField()).willReturn("testValue"); @@ -116,7 +120,7 @@ void doNotRemoveImportsAssociatedWithAnUntypedMockitoMethodMixed() { class MyObjectTest { MyObject myObject; MyMockClass myMock; - + void test() { when(myObject.getSomeField()).thenReturn("testValue"); given(myObject.getSomeField()).willReturn("testValue"); @@ -148,7 +152,7 @@ void doNotRemoveImportsAssociatedWithATypedMockitoMethodMixed() { class MyObjectTest { MyObject myObject; MyMockClass myMock; - + void test() { when(myObject.getSomeField()).thenReturn("testValue"); given(myObject.getSomeField()).willReturn("testValue"); @@ -178,7 +182,7 @@ void doNotRemoveStartImportsPossiblyAssociatedWithAnUntypedMockitoMethod() { class MyObjectTest { MyObject myObject; - + void test() { when(myObject.getSomeField()).thenReturn("testValue"); } @@ -212,7 +216,7 @@ class MyObject { class MyObjectTest { @Mock MyObject myObject; - + void test() { when(myObject.getSomeField()).thenReturn("testValue"); } @@ -226,7 +230,7 @@ void test() { class MyObjectTest { @Mock MyObject myObject; - + void test() { when(myObject.getSomeField()).thenReturn("testValue"); } @@ -243,16 +247,16 @@ void preserveStarImports() { java( """ package mockito.example; - + import java.util.List; - + import static org.mockito.Mockito.*; - + public class MockitoArgumentMatchersTest { static class Foo { boolean bool(String str, int i, Object obj) { return false; } } - + public void usesMatchers() { Foo mockFoo = mock(Foo.class); when(mockFoo.bool(anyString(), anyInt(), any(Object.class))).thenReturn(true); @@ -270,7 +274,7 @@ void removeUnusedStarImports() { java( """ import static org.mockito.Mockito.*; - + public class MockitoArgumentMatchersTest { } """, @@ -281,4 +285,28 @@ public class MockitoArgumentMatchersTest { ) ); } + + @Test + void handleKotlinImportsCorrectly() { + rewriteRun( + //language=kotlin + kotlin( + """ + import org.mockito.kotlin.times + class Foo { + fun bar() { + org.mockito.Mockito.mock(Foo::class.java) + } + } + """, + """ + class Foo { + fun bar() { + org.mockito.Mockito.mock(Foo::class.java) + } + } + """ + ) + ); + } }