diff --git a/build.gradle.kts b/build.gradle.kts
index 71a58d2a0..f0ad04331 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -19,6 +19,7 @@ recipeDependencies {
parserClasspath("com.github.tomakehurst:wiremock-jre8:2.35.0")
parserClasspath("org.mockito:mockito-all:1.10.19")
parserClasspath("org.mockito:mockito-core:3.+")
+ parserClasspath("org.mockito:mockito-core:5.+")
parserClasspath("org.jmockit:jmockit:1.49")
parserClasspath("org.jmockit:jmockit:1.22") // last version with NonStrictExpectations
parserClasspath("org.mockito:mockito-junit-jupiter:3.+")
diff --git a/src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockType.java b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockType.java
index c691d97b6..7b0cbb666 100644
--- a/src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockType.java
+++ b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockType.java
@@ -22,6 +22,7 @@
@Getter
enum JMockitBlockType {
+ MockUp,
Expectations,
NonStrictExpectations,
Verifications,
diff --git a/src/main/java/org/openrewrite/java/testing/jmockit/JMockitMockUpToMockito.java b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitMockUpToMockito.java
new file mode 100644
index 000000000..c96917adf
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/testing/jmockit/JMockitMockUpToMockito.java
@@ -0,0 +1,545 @@
+/*
+ * 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.SneakyThrows;
+import org.openrewrite.java.search.UsesType;
+import static org.openrewrite.java.testing.jmockit.JMockitBlockType.MockUp;
+import static org.openrewrite.java.testing.mockito.MockitoUtils.maybeAddMethodWithAnnotation;
+import static org.openrewrite.java.tree.Flag.Private;
+import static org.openrewrite.java.tree.Flag.Static;
+import org.openrewrite.java.tree.*;
+import org.openrewrite.marker.SearchResult;
+import org.openrewrite.staticanalysis.LambdaBlockToExpression;
+import org.openrewrite.staticanalysis.RemoveUnusedLocalVariables;
+
+import java.lang.reflect.Method;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static java.util.stream.Collectors.toList;
+import static java.util.stream.Collectors.toMap;
+import static org.openrewrite.java.testing.jmockit.JMockitBlockType.MockUp;
+import static org.openrewrite.java.testing.mockito.MockitoUtils.maybeAddMethodWithAnnotation;
+import static org.openrewrite.java.tree.Flag.Private;
+import static org.openrewrite.java.tree.Flag.Static;
+
+public class JMockitMockUpToMockito extends Recipe {
+ private static final String MOCKITO_CLASSPATH = "mockito-core-5";
+ private static final String MOCKITO_ALL_IMPORT = "org.mockito.Mockito.*";
+ private static final String MOCKITO_MATCHER_IMPORT = "org.mockito.ArgumentMatchers.*";
+ private static final String MOCKITO_DELEGATEANSWER_IMPORT = "org.mockito.AdditionalAnswers.delegatesTo";
+ private static final String JMOCKIT_MOCKUP_IMPORT = MockUp.getFqn();
+ private static final String JMOCKIT_MOCK_IMPORT = "mockit.Mock";
+ private static final String MOCKITO_STATIC_PREFIX = "mockStatic";
+ private static final String MOCKITO_STATIC_IMPORT = "org.mockito.MockedStatic";
+ private static final String MOCKITO_MOCK_PREFIX = "mock";
+ private static final String MOCKITO_CONSTRUCTION_PREFIX = "mockCons";
+ private static final String MOCKITO_CONSTRUCTION_IMPORT = "org.mockito.MockedConstruction";
+
+ private static final String TEARDOWN_METHOD_ANNOTATION_SIGNATURE = "@org.junit.After";
+ private static final String TEARDOWN_METHOD_ANNOTATION_TO_ADD = "@After";
+ private static final String TEARDOWN_CLASSPATH_RESOURCE = "junit-4.13";
+ private static final String TEARDOWN_IMPORT_TO_ADD = "org.junit.After";
+
+ @Override
+ public String getDisplayName() {
+ return "Rewrite JMockit MockUp to Mockito statements";
+ }
+
+ @Override
+ public String getDescription() {
+ return "Rewrites JMockit `MockUp` blocks to Mockito statements. This recipe will not rewrite private methods in MockUp.";
+ }
+
+ @Override
+ public TreeVisitor, ExecutionContext> getVisitor() {
+ return Preconditions.check(new UsesType<>(MockUp.getFqn(), false), new JMockitMockUpToMockitoVisitor());
+ }
+
+ private static class JMockitMockUpToMockitoVisitor extends JavaIsoVisitor {
+ private final Map tearDownMocks = new HashMap<>();
+
+ /**
+ * Handle at class level because need to handle the case where when there is a MockUp in a setup method, and we
+ * need to close the migrated mockCons in the teardown, yet the teardown method comes before the setup method
+ */
+ @Override
+ public J.ClassDeclaration visitClassDeclaration(J.ClassDeclaration classDecl, ExecutionContext ctx) {
+ // Handle @Before/@BeforeEach mockUp
+ Set mds = TreeVisitor.collect(
+ new JavaIsoVisitor() {
+ @Override
+ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration md, ExecutionContext ctx) {
+ if (isSetUpMethod(md)) {
+ return SearchResult.found(md);
+ }
+ return super.visitMethodDeclaration(md, ctx);
+ }
+ },
+ classDecl,
+ new HashSet<>()
+ )
+ .stream()
+ .filter(J.MethodDeclaration.class::isInstance)
+ .map(J.MethodDeclaration.class::cast)
+ .collect(Collectors.toSet());
+ if (mds.isEmpty()) {
+ return super.visitClassDeclaration(classDecl, ctx);
+ }
+
+ final J.ClassDeclaration[] cd = {classDecl};
+ mds.forEach(md -> md.getBody()
+ .getStatements()
+ .stream()
+ .filter(this::isMockUpStatement)
+ .map(J.NewClass.class::cast)
+ .forEach(newClass -> {
+ String className = ((J.ParameterizedType) newClass.getClazz()).getTypeParameters().get(0).toString();
+
+ Map mockedMethods = getMockUpMethods(newClass);
+
+ // Add mockStatic field
+ if (mockedMethods.values().stream().anyMatch(m -> m.getFlags().contains(Static))) {
+ cd[0] = JavaTemplate.builder("private MockedStatic #{};")
+ .contextSensitive()
+ .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, MOCKITO_CLASSPATH))
+ .imports(MOCKITO_STATIC_IMPORT)
+ .staticImports(MOCKITO_ALL_IMPORT)
+ .build()
+ .apply(
+ new Cursor(getCursor().getParentOrThrow(), cd[0]),
+ cd[0].getBody().getCoordinates().firstStatement(),
+ MOCKITO_STATIC_PREFIX + className
+ );
+ J.VariableDeclarations mockField = (J.VariableDeclarations) cd[0].getBody().getStatements().get(0);
+ J.Identifier mockFieldId = mockField.getVariables().get(0).getName();
+ tearDownMocks.put(MOCKITO_STATIC_PREFIX + className, mockFieldId);
+ }
+ // Add mockConstruction field
+ if (mockedMethods.values().stream().anyMatch(m -> !m.getFlags().contains(Static))) {
+ cd[0] = JavaTemplate.builder("private MockedConstruction #{};")
+ .contextSensitive()
+ .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, MOCKITO_CLASSPATH))
+ .imports(MOCKITO_CONSTRUCTION_IMPORT)
+ .staticImports(MOCKITO_ALL_IMPORT)
+ .build()
+ .apply(
+ updateCursor(cd[0]),
+ cd[0].getBody().getCoordinates().firstStatement(),
+ MOCKITO_CONSTRUCTION_PREFIX + className
+ );
+ J.VariableDeclarations mockField = (J.VariableDeclarations) cd[0].getBody().getStatements().get(0);
+ J.Identifier mockFieldId = mockField.getVariables().get(0).getName();
+ tearDownMocks.put(MOCKITO_CONSTRUCTION_PREFIX + className, mockFieldId);
+ }
+ }));
+
+ cd[0] = maybeAddMethodWithAnnotation(this, cd[0], ctx, "tearDown",
+ TEARDOWN_METHOD_ANNOTATION_SIGNATURE,
+ TEARDOWN_METHOD_ANNOTATION_TO_ADD,
+ TEARDOWN_CLASSPATH_RESOURCE,
+ TEARDOWN_IMPORT_TO_ADD,
+ "");
+
+ return super.visitClassDeclaration(cd[0], ctx);
+ }
+
+ @Override
+ public J.MethodDeclaration visitMethodDeclaration(J.MethodDeclaration methodDecl, ExecutionContext ctx) {
+ J.MethodDeclaration md = methodDecl;
+ if (md.getBody() == null) {
+ return md;
+ }
+ if (isTearDownMethod(md)) {
+ for (J.Identifier id : tearDownMocks.values()) {
+ String type = TypeUtils.asFullyQualified(id.getFieldType().getType()).getFullyQualifiedName();
+ md = JavaTemplate.builder("#{any(" + type + ")}.closeOnDemand();")
+ .contextSensitive()
+ .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, MOCKITO_CLASSPATH))
+ .imports(MOCKITO_STATIC_IMPORT, MOCKITO_CONSTRUCTION_IMPORT)
+ .staticImports(MOCKITO_ALL_IMPORT)
+ .build()
+ .apply(
+ updateCursor(md),
+ md.getBody().getCoordinates().lastStatement(),
+ id
+ );
+ }
+ return md;
+ }
+
+ boolean isBeforeTest = isSetUpMethod(md);
+ List varDeclarationInTry = new ArrayList<>();
+ List mockStaticMethodInTry = new ArrayList<>();
+ List mockConstructionMethodInTry = new ArrayList<>();
+ List encloseStatements = new ArrayList<>();
+ List residualStatements = new ArrayList<>();
+ for (Statement statement : md.getBody().getStatements()) {
+ if (!isMockUpStatement(statement)) {
+ encloseStatements.add(statement);
+ continue;
+ }
+
+ J.NewClass newClass = (J.NewClass) statement;
+
+ // Only discard @Mock method declarations
+ residualStatements.addAll(newClass
+ .getBody()
+ .getStatements()
+ .stream()
+ .filter(s -> {
+ if (s instanceof J.MethodDeclaration) {
+ return ((J.MethodDeclaration) s).getLeadingAnnotations().stream()
+ .noneMatch(o -> TypeUtils.isOfClassType(o.getType(), JMOCKIT_MOCK_IMPORT));
+ }
+ return true;
+ })
+ .collect(toList())
+ );
+
+ JavaType mockType = ((J.ParameterizedType) newClass.getClazz()).getTypeParameters().get(0).getType();
+ String className = ((J.ParameterizedType) newClass.getClazz()).getTypeParameters().get(0).toString();
+
+ Map mockedMethods = getMockUpMethods(newClass);
+
+ // Add MockStatic
+ Map mockedPublicStaticMethods = mockedMethods
+ .entrySet()
+ .stream()
+ .filter(m -> m.getValue().getFlags().contains(Static))
+ .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
+ if (!mockedPublicStaticMethods.isEmpty()) {
+ if (isBeforeTest) {
+ String tpl = getMockStaticDeclarationInBefore(className) +
+ getMockStaticMethods((JavaType.Class) mockType, className, mockedPublicStaticMethods);
+
+ md = JavaTemplate.builder(tpl)
+ .contextSensitive()
+ .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, MOCKITO_CLASSPATH))
+ .imports(MOCKITO_STATIC_IMPORT)
+ .staticImports(MOCKITO_ALL_IMPORT)
+ .build()
+ .apply(
+ updateCursor(md),
+ statement.getCoordinates().after(),
+ tearDownMocks.get(MOCKITO_STATIC_PREFIX + className)
+ );
+ } else {
+ varDeclarationInTry.add(getMockStaticDeclarationInTry(className));
+ mockStaticMethodInTry.add(getMockStaticMethods((JavaType.Class) mockType, className, mockedPublicStaticMethods));
+ }
+
+ maybeAddImport(MOCKITO_STATIC_IMPORT);
+ }
+
+ // Add MockConstruction
+ Map mockedPublicMethods = mockedMethods
+ .entrySet()
+ .stream()
+ .filter(m -> !m.getValue().getFlags().contains(Static))
+ .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
+ if (!mockedPublicMethods.isEmpty()) {
+ if (isBeforeTest) {
+ String tpl = getMockConstructionMethods(className, mockedPublicMethods) +
+ getMockConstructionDeclarationInBefore(className);
+
+ md = JavaTemplate.builder(tpl)
+ .contextSensitive()
+ .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, MOCKITO_CLASSPATH))
+ .imports(MOCKITO_STATIC_IMPORT)
+ .staticImports(MOCKITO_ALL_IMPORT, MOCKITO_DELEGATEANSWER_IMPORT)
+ .build()
+ .apply(
+ updateCursor(md),
+ statement.getCoordinates().after(),
+ tearDownMocks.get(MOCKITO_CONSTRUCTION_PREFIX + className)
+ );
+ } else {
+ varDeclarationInTry.add(getMockConstructionDeclarationInTry(className));
+ mockConstructionMethodInTry.add(getMockConstructionMethods(className, mockedPublicMethods));
+ }
+
+ maybeAddImport(MOCKITO_CONSTRUCTION_IMPORT);
+ maybeAddImport("org.mockito.Answers", "CALLS_REAL_METHODS", false);
+ maybeAddImport("org.mockito.AdditionalAnswers", "delegatesTo", false);
+ }
+
+ List statements = md.getBody().getStatements();
+ statements.remove(statement);
+ md = md.withBody(md.getBody().withStatements(statements));
+ }
+
+ if (!varDeclarationInTry.isEmpty()) {
+ String tpl = String.join("", mockConstructionMethodInTry) +
+ "try (" +
+ String.join(";", varDeclarationInTry) +
+ ") {" +
+ String.join(";", mockStaticMethodInTry) +
+ "}";
+
+ J.MethodDeclaration residualMd = md.withBody(md.getBody().withStatements(residualStatements));
+ residualMd = JavaTemplate.builder(tpl)
+ .contextSensitive()
+ .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, MOCKITO_CLASSPATH))
+ .imports(MOCKITO_STATIC_IMPORT, MOCKITO_CONSTRUCTION_IMPORT)
+ .staticImports(MOCKITO_ALL_IMPORT, MOCKITO_MATCHER_IMPORT, MOCKITO_MATCHER_IMPORT, MOCKITO_DELEGATEANSWER_IMPORT)
+ .build()
+ .apply(updateCursor(residualMd), residualMd.getBody().getCoordinates().lastStatement());
+
+ List mdStatements = residualMd.getBody().getStatements();
+ J.Try try_ = (J.Try) mdStatements.get(mdStatements.size() - 1);
+
+ List tryStatements = try_.getBody().getStatements();
+ tryStatements.addAll(encloseStatements);
+ try_ = try_.withBody(try_.getBody().withStatements(tryStatements));
+
+ mdStatements.set(mdStatements.size() - 1, try_);
+ md = md.withBody(residualMd.getBody().withStatements(mdStatements));
+ }
+
+ maybeAddImport(MOCKITO_ALL_IMPORT.replace(".*", ""), "*", false);
+ maybeRemoveImport(JMOCKIT_MOCK_IMPORT);
+ maybeRemoveImport(JMOCKIT_MOCKUP_IMPORT);
+
+ doAfterVisit(new LambdaBlockToExpression().getVisitor());
+ return maybeAutoFormat(methodDecl, md, ctx);
+ }
+
+ private String getMatcher(JavaType s) {
+ maybeAddImport(MOCKITO_MATCHER_IMPORT.replace(".*", ""), "*", false);
+ if (s instanceof JavaType.Primitive) {
+ switch (s.toString()) {
+ case "int":
+ return "anyInt()";
+ case "long":
+ return "anyLong()";
+ case "double":
+ return "anyDouble()";
+ case "float":
+ return "anyFloat()";
+ case "short":
+ return "anyShort()";
+ case "byte":
+ return "anyByte()";
+ case "char":
+ return "anyChar()";
+ case "boolean":
+ return "anyBoolean()";
+ }
+ } else if (s instanceof JavaType.Array) {
+ String elem = TypeUtils.asArray(s).getElemType().toString();
+ return "nullable(" + elem + "[].class)";
+ }
+ return "nullable(" + TypeUtils.asFullyQualified(s).getClassName() + ".class)";
+ }
+
+ @SneakyThrows
+ private String getAnswerBody(J.MethodDeclaration md) {
+ Method findRefs = RemoveUnusedLocalVariables.class
+ .getDeclaredClasses()[0]
+ .getDeclaredMethod("findRhsReferences", J.class, J.Identifier.class);
+ findRefs.setAccessible(true);
+
+ Set usedVariables = new HashSet<>();
+ (new JavaIsoVisitor>() {
+ @Override
+ @SneakyThrows
+ public J.VariableDeclarations.NamedVariable visitVariable(J.VariableDeclarations.NamedVariable variable, Set ctx) {
+ Cursor scope = getCursor().dropParentUntil((is) -> is instanceof J.ClassDeclaration || is instanceof J.Block || is instanceof J.MethodDeclaration || is instanceof J.ForLoop || is instanceof J.ForEachLoop || is instanceof J.ForLoop.Control || is instanceof J.ForEachLoop.Control || is instanceof J.Case || is instanceof J.Try || is instanceof J.Try.Resource || is instanceof J.Try.Catch || is instanceof J.MultiCatch || is instanceof J.Lambda || is instanceof JavaSourceFile);
+ List refs = (List) findRefs.invoke(null, scope.getValue(), variable.getName());
+ if (!refs.isEmpty()) {
+ ctx.add(variable.getSimpleName());
+ }
+ return super.visitVariable(variable, ctx);
+ }
+ }).visit(md, usedVariables);
+
+ StringBuilder sb = new StringBuilder();
+ List parameters = md.getParameters();
+ for (int i = 0; i < parameters.size(); i++) {
+ if (!(parameters.get(i) instanceof J.VariableDeclarations)) {
+ continue;
+ }
+ J.VariableDeclarations vd = (J.VariableDeclarations) parameters.get(i);
+ String className;
+ if (vd.getType() instanceof JavaType.Primitive) {
+ className = vd.getType().toString();
+ } else {
+ className = vd.getTypeAsFullyQualified().getClassName();
+ }
+ String varName = vd.getVariables().get(0).getName().getSimpleName();
+ if (usedVariables.contains(varName)) {
+ sb.append(className).append(" ").append(varName)
+ .append(" = invocation.getArgument(").append(i).append(");");
+ }
+ }
+
+ boolean hasReturn = false;
+ for (Statement s : md.getBody().getStatements()) {
+ hasReturn = hasReturn || s instanceof J.Return;
+ sb.append(s.print(getCursor())).append(";");
+ }
+ // Avoid syntax error
+ if (!hasReturn) {
+ sb.append("return null;");
+ }
+ return sb.toString();
+ }
+
+ private String getCallRealMethod(JavaType.Method m) {
+ return "(" +
+ m.getParameterTypes()
+ .stream()
+ .map(this::getMatcher)
+ .collect(Collectors.joining(", ")) +
+ ")).thenCallRealMethod();";
+ }
+
+ private String getMockStaticDeclarationInBefore(String className) {
+ return "#{any(" + MOCKITO_STATIC_IMPORT + ")}" +
+ " = mockStatic(" + className + ".class);";
+ }
+
+ private String getMockStaticDeclarationInTry(String className) {
+ return "MockedStatic " + MOCKITO_STATIC_PREFIX + className +
+ " = mockStatic(" + className + ".class)";
+ }
+
+ private String getMockStaticMethods(JavaType.Class clazz, String className, Map mockedMethods) {
+ StringBuilder tpl = new StringBuilder();
+
+ // To generate predictable method order
+ List keys = mockedMethods.keySet().stream()
+ .sorted(Comparator.comparing(o -> o.print(getCursor())))
+ .collect(toList());
+ for (J.MethodDeclaration m : keys) {
+ tpl.append("mockStatic").append(className)
+ .append(".when(() -> ").append(className).append(".").append(m.getSimpleName()).append("(")
+ .append(m.getParameters()
+ .stream()
+ .filter(J.VariableDeclarations.class::isInstance)
+ .map(J.VariableDeclarations.class::cast)
+ .map(J.VariableDeclarations::getType)
+ .map(this::getMatcher)
+ .collect(Collectors.joining(", "))
+ )
+ .append(")).thenAnswer(invocation -> {")
+ .append(getAnswerBody(m))
+ .append("});");
+ }
+
+ // Call real method for non private, static methods
+ clazz.getMethods()
+ .stream()
+ .filter(m -> !m.isConstructor())
+ .filter(m -> !m.getFlags().contains(Private))
+ .filter(m -> m.getFlags().contains(Static))
+ .filter(m -> !mockedMethods.containsValue(m))
+ .forEach(m -> tpl.append("mockStatic").append(className).append(".when(() -> ")
+ .append(className).append(".").append(m.getName())
+ .append(getCallRealMethod(m))
+ .append(");")
+ );
+
+ return tpl.toString();
+ }
+
+ private String getMockConstructionDeclarationInBefore(String className) {
+ return "#{any(" + MOCKITO_CONSTRUCTION_IMPORT + ")}" +
+ " = mockConstructionWithAnswer(" + className + ".class, delegatesTo(" + MOCKITO_MOCK_PREFIX + className + "));";
+ }
+
+ private String getMockConstructionDeclarationInTry(String className) {
+ return "MockedConstruction " + MOCKITO_CONSTRUCTION_PREFIX + className +
+ " = mockConstructionWithAnswer(" + className + ".class, delegatesTo(" + MOCKITO_MOCK_PREFIX + className + "))";
+ }
+
+ private String getMockConstructionMethods(String className, Map mockedMethods) {
+ StringBuilder tpl = new StringBuilder()
+ .append(className)
+ .append(" ")
+ .append(MOCKITO_MOCK_PREFIX).append(className)
+ .append(" = mock(").append(className).append(".class, CALLS_REAL_METHODS);");
+
+ mockedMethods
+ .keySet()
+ .stream()
+ .sorted(Comparator.comparing(o -> o.print(getCursor())))
+ .forEach(m -> tpl.append("doAnswer(invocation -> {")
+ .append(getAnswerBody(m))
+ .append("}).when(").append(MOCKITO_MOCK_PREFIX).append(className).append(").").append(m.getSimpleName()).append("(")
+ .append(m.getParameters()
+ .stream()
+ .filter(J.VariableDeclarations.class::isInstance)
+ .map(J.VariableDeclarations.class::cast)
+ .map(J.VariableDeclarations::getType)
+ .map(this::getMatcher)
+ .collect(Collectors.joining(", "))
+ )
+ .append(");"));
+
+ return tpl.toString();
+ }
+
+ private boolean isMockUpStatement(Tree tree) {
+ return tree instanceof J.NewClass &&
+ ((J.NewClass) tree).getClazz() != null &&
+ TypeUtils.isOfClassType(((J.NewClass) tree).getClazz().getType(), JMOCKIT_MOCKUP_IMPORT);
+ }
+
+ private boolean isSetUpMethod(J.MethodDeclaration md) {
+ return md
+ .getLeadingAnnotations()
+ .stream()
+ .anyMatch(o -> TypeUtils.isOfClassType(o.getType(), "org.junit.Before"));
+ }
+
+ private boolean isTearDownMethod(J.MethodDeclaration md) {
+ return md
+ .getLeadingAnnotations()
+ .stream()
+ .anyMatch(o -> TypeUtils.isOfClassType(o.getType(), "org.junit.After"));
+ }
+
+ private Map getMockUpMethods(J.NewClass newClass) {
+ JavaType mockType = ((J.ParameterizedType) newClass.getClazz()).getTypeParameters().get(0).getType();
+ return newClass.getBody()
+ .getStatements()
+ .stream()
+ .filter(J.MethodDeclaration.class::isInstance)
+ .map(J.MethodDeclaration.class::cast)
+ .filter(s -> s.getLeadingAnnotations().stream()
+ .anyMatch(o -> TypeUtils.isOfClassType(o.getType(), JMOCKIT_MOCK_IMPORT)))
+ .map(method -> {
+ Optional found = TypeUtils.findDeclaredMethod(
+ TypeUtils.asFullyQualified(mockType),
+ method.getSimpleName(),
+ method.getMethodType().getParameterTypes()
+ );
+ if (found.isPresent()) {
+ JavaType.Method m = found.get();
+ if (!m.getFlags().contains(Private)) {
+ return new AbstractMap.SimpleEntry<>(method, found.get());
+ }
+ }
+ return null;
+ })
+ .filter(Objects::nonNull)
+ .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+ }
+}
diff --git a/src/main/java/org/openrewrite/java/testing/mockito/MockitoUtils.java b/src/main/java/org/openrewrite/java/testing/mockito/MockitoUtils.java
new file mode 100644
index 000000000..52b22fdfa
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/testing/mockito/MockitoUtils.java
@@ -0,0 +1,90 @@
+/*
+ * 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.mockito;
+
+import org.jspecify.annotations.Nullable;
+import org.openrewrite.Cursor;
+import org.openrewrite.ExecutionContext;
+import org.openrewrite.java.AnnotationMatcher;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.java.JavaTemplate;
+import org.openrewrite.java.JavaVisitor;
+import org.openrewrite.java.tree.J;
+import org.openrewrite.java.tree.Statement;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class MockitoUtils {
+ public static J.ClassDeclaration maybeAddMethodWithAnnotation(
+ JavaVisitor visitor,
+ J.ClassDeclaration classDecl,
+ ExecutionContext ctx,
+ String methodName,
+ String methodAnnotationSignature,
+ String methodAnnotationToAdd,
+ String additionalClasspathResource,
+ String importToAdd,
+ String methodAnnotationParameters
+ ) {
+ if (hasMethodWithAnnotation(classDecl, new AnnotationMatcher(methodAnnotationSignature))) {
+ return classDecl;
+ }
+
+ J.MethodDeclaration firstTestMethod = getFirstTestMethod(
+ classDecl.getBody().getStatements().stream().filter(J.MethodDeclaration.class::isInstance)
+ .map(J.MethodDeclaration.class::cast).collect(Collectors.toList()));
+
+ visitor.maybeAddImport(importToAdd);
+ return JavaTemplate.builder(methodAnnotationToAdd + methodAnnotationParameters + " public void " + methodName + "() {}")
+ .contextSensitive()
+ .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, additionalClasspathResource))
+ .imports(importToAdd)
+ .build()
+ .apply(
+ new Cursor(visitor.getCursor().getParentOrThrow(), classDecl),
+ (firstTestMethod != null) ?
+ firstTestMethod.getCoordinates().before() :
+ classDecl.getBody().getCoordinates().lastStatement()
+ );
+ }
+
+ private static boolean hasMethodWithAnnotation(J.ClassDeclaration classDecl, AnnotationMatcher annotationMatcher) {
+ for (Statement statement : classDecl.getBody().getStatements()) {
+ if (statement instanceof J.MethodDeclaration) {
+ J.MethodDeclaration methodDeclaration = (J.MethodDeclaration) statement;
+ List allAnnotations = methodDeclaration.getAllAnnotations();
+ for (J.Annotation annotation : allAnnotations) {
+ if (annotationMatcher.matches(annotation)) {
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+ }
+
+ private static J.@Nullable MethodDeclaration getFirstTestMethod(List methods) {
+ for (J.MethodDeclaration methodDeclaration : methods) {
+ for (J.Annotation annotation : methodDeclaration.getLeadingAnnotations()) {
+ if ("Test".equals(annotation.getSimpleName())) {
+ return methodDeclaration;
+ }
+ }
+ }
+ return null;
+ }
+}
diff --git a/src/main/java/org/openrewrite/java/testing/mockito/PowerMockitoMockStaticToMockito.java b/src/main/java/org/openrewrite/java/testing/mockito/PowerMockitoMockStaticToMockito.java
index 019425f50..4305eabc1 100644
--- a/src/main/java/org/openrewrite/java/testing/mockito/PowerMockitoMockStaticToMockito.java
+++ b/src/main/java/org/openrewrite/java/testing/mockito/PowerMockitoMockStaticToMockito.java
@@ -26,6 +26,8 @@
import java.util.*;
import java.util.stream.Collectors;
+import static org.openrewrite.java.testing.mockito.MockitoUtils.maybeAddMethodWithAnnotation;
+
public class PowerMockitoMockStaticToMockito extends Recipe {
@Override
@@ -36,17 +38,17 @@ public String getDisplayName() {
@Override
public String getDescription() {
return "Replaces `PowerMockito.mockStatic()` by `Mockito.mockStatic()`. Removes " +
- "the `@PrepareForTest` annotation.";
+ "the `@PrepareForTest` annotation.";
}
@Override
public TreeVisitor, ExecutionContext> getVisitor() {
return Preconditions.check(
- Preconditions.or(
- new UsesType<>("org.powermock..*", false),
- new UsesType<>("org.mockito..*", false)
- ),
- new PowerMockitoToMockitoVisitor()
+ Preconditions.or(
+ new UsesType<>("org.powermock..*", false),
+ new UsesType<>("org.mockito..*", false)
+ ),
+ new PowerMockitoToMockitoVisitor()
);
}
@@ -61,9 +63,9 @@ private static class PowerMockitoToMockitoVisitor extends JavaVisitor methods) {
- for (J.MethodDeclaration methodDeclaration : methods) {
- for (J.Annotation annotation : methodDeclaration.getLeadingAnnotations()) {
- if ("Test".equals(annotation.getSimpleName())) {
- return methodDeclaration;
- }
- }
- }
- return null;
- }
-
- private static boolean hasMethodWithAnnotation(J.ClassDeclaration classDecl, AnnotationMatcher annotationMatcher) {
- for (Statement statement : classDecl.getBody().getStatements()) {
- if (statement instanceof J.MethodDeclaration) {
- J.MethodDeclaration methodDeclaration = (J.MethodDeclaration) statement;
- List allAnnotations = methodDeclaration.getAllAnnotations();
- for (J.Annotation annotation : allAnnotations) {
- if (annotationMatcher.matches(annotation)) {
- return true;
- }
- }
- }
- }
- return false;
- }
-
private static boolean isStaticMockAlreadyClosed(J.Identifier staticMock, J.Block methodBody) {
for (Statement statement : methodBody.getStatements()) {
if (statement instanceof J.MethodInvocation) {
@@ -246,7 +222,7 @@ private static boolean isStaticMockAlreadyClosed(J.Identifier staticMock, J.Bloc
if (MOCKED_STATIC_CLOSE_MATCHER.matches(methodInvocation)) {
if (methodInvocation.getSelect() instanceof J.Identifier) {
if (((J.Identifier) methodInvocation.getSelect()).getSimpleName()
- .equals(staticMock.getSimpleName())) {
+ .equals(staticMock.getSimpleName())) {
return true;
}
}
@@ -263,7 +239,7 @@ private static boolean isStaticMockAlreadyOpened(J.Identifier staticMock, J.Bloc
if (MOCKED_STATIC_MATCHER.matches(methodInvocation)) {
if (methodInvocation.getSelect() instanceof J.Identifier) {
if (((J.Identifier) methodInvocation.getSelect()).getSimpleName()
- .equals(staticMock.getSimpleName())) {
+ .equals(staticMock.getSimpleName())) {
return true;
}
}
@@ -288,15 +264,15 @@ private J.MethodDeclaration moveMockStaticMethodToSetUp(J.MethodDeclaration m, E
J.MethodInvocation methodInvocation = mockStaticInvocations.get(className);
if (methodInvocation != null) {
m = JavaTemplate.builder("mocked#{any(org.mockito.MockedStatic)} = #{any(org.mockito.Mockito)};")
- .contextSensitive()
- .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12"))
- .build()
- .apply(
- new Cursor(getCursor().getParentOrThrow(), m),
- methodBody.getCoordinates().firstStatement(),
- mockedTypesFieldEntry.getKey(),
- methodInvocation
- );
+ .contextSensitive()
+ .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12"))
+ .build()
+ .apply(
+ new Cursor(getCursor().getParentOrThrow(), m),
+ methodBody.getCoordinates().firstStatement(),
+ mockedTypesFieldEntry.getKey(),
+ methodInvocation
+ );
}
}
}
@@ -311,14 +287,14 @@ private J.MethodDeclaration addCloseStaticMocksOnDemandStatement(J.MethodDeclara
continue;
}
m = JavaTemplate.builder("#{any(org.mockito.MockedStatic)}.closeOnDemand();")
- .contextSensitive()
- .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12"))
- .build()
- .apply(
- new Cursor(getCursor().getParentOrThrow(), m),
- methodBody.getCoordinates().lastStatement(),
- mockedTypesField.getKey()
- );
+ .contextSensitive()
+ .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12"))
+ .build()
+ .apply(
+ new Cursor(getCursor().getParentOrThrow(), m),
+ methodBody.getCoordinates().lastStatement(),
+ mockedTypesField.getKey()
+ );
}
return m;
}
@@ -328,14 +304,14 @@ private void determineTestGroups() {
J.MethodDeclaration methodDeclarationCursor = getCursor().firstEnclosing(J.MethodDeclaration.class);
if (methodDeclarationCursor != null) {
Optional testAnnotation = methodDeclarationCursor
- .getLeadingAnnotations().stream()
- .filter(annotation -> annotation.getSimpleName().equals("Test")).findFirst();
+ .getLeadingAnnotations().stream()
+ .filter(annotation -> annotation.getSimpleName().equals("Test")).findFirst();
testAnnotation.ifPresent(
- ta -> {
- if (ta.getArguments() != null) {
- getCursor().putMessageOnFirstEnclosing(J.ClassDeclaration.class, TEST_GROUP, ta.getArguments());
- }
- });
+ ta -> {
+ if (ta.getArguments() != null) {
+ getCursor().putMessageOnFirstEnclosing(J.ClassDeclaration.class, TEST_GROUP, ta.getArguments());
+ }
+ });
}
}
}
@@ -354,15 +330,15 @@ private J.MethodInvocation modifyDynamicWhenMethodInvocation(J.MethodInvocation
arguments.remove(0);
String stringOfArguments = arguments.stream().map(Object::toString).collect(Collectors.joining(","));
method = JavaTemplate.builder("() -> #{}.#{}(#{})")
- .contextSensitive()
- .build()
- .apply(
- new Cursor(getCursor().getParentOrThrow(), method),
- method.getCoordinates().replaceArguments(),
- declaringClassName,
- Objects.requireNonNull(calledMethod.getValue()).toString(),
- stringOfArguments
- );
+ .contextSensitive()
+ .build()
+ .apply(
+ new Cursor(getCursor().getParentOrThrow(), method),
+ method.getCoordinates().replaceArguments(),
+ declaringClassName,
+ Objects.requireNonNull(calledMethod.getValue()).toString(),
+ stringOfArguments
+ );
method = method.withSelect(mockedField);
}
return method;
@@ -439,17 +415,17 @@ private J.ClassDeclaration addFieldDeclarationForMockedTypes(J.ClassDeclaration
continue;
}
classDecl = JavaTemplate.builder("private MockedStatic<#{}> " + MOCK_PREFIX + "#{};")
- .contextSensitive()
- .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12"))
- .staticImports("org.mockito.Mockito.mockStatic")
- .imports(MOCKED_STATIC)
- .build()
- .apply(
- new Cursor(getCursor().getParentOrThrow(), classDecl),
- classDecl.getBody().getCoordinates().firstStatement(),
- classlessTypeName,
- classlessTypeName.replace(".", "_")
- );
+ .contextSensitive()
+ .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12"))
+ .staticImports("org.mockito.Mockito.mockStatic")
+ .imports(MOCKED_STATIC)
+ .build()
+ .apply(
+ new Cursor(getCursor().getParentOrThrow(), classDecl),
+ classDecl.getBody().getCoordinates().firstStatement(),
+ classlessTypeName,
+ classlessTypeName.replace(".", "_")
+ );
J.VariableDeclarations mockField = (J.VariableDeclarations) classDecl.getBody().getStatements().get(0);
mockedTypesIdentifiers.put(mockField.getVariables().get(0).getName(), mockedStaticClass);
@@ -457,7 +433,7 @@ private J.ClassDeclaration addFieldDeclarationForMockedTypes(J.ClassDeclaration
getCursor().putMessage(MOCKED_TYPES_FIELDS, mockedTypesIdentifiers);
maybeAutoFormat(classDecl, classDecl.withPrefix(classDecl.getPrefix().
- withWhitespace("")), classDecl.getName(), ctx, getCursor());
+ withWhitespace("")), classDecl.getName(), ctx, getCursor());
maybeAddImport(MOCKED_STATIC);
maybeAddImport("org.mockito.Mockito", "mockStatic");
return classDecl;
@@ -465,9 +441,9 @@ private J.ClassDeclaration addFieldDeclarationForMockedTypes(J.ClassDeclaration
private J.ClassDeclaration maybeAddSetUpMethodBody(J.ClassDeclaration classDecl, ExecutionContext ctx) {
String testGroupsAsString = getTestGroupsAsString();
- return maybeAddMethodWithAnnotation(classDecl, ctx, "setUpStaticMocks",
- setUpMethodAnnotationSignature, setUpMethodAnnotation,
- additionalClasspathResource, setUpImportToAdd, testGroupsAsString);
+ return maybeAddMethodWithAnnotation(this, classDecl, ctx, "setUpStaticMocks",
+ setUpMethodAnnotationSignature, setUpMethodAnnotation,
+ additionalClasspathResource, setUpImportToAdd, testGroupsAsString);
}
private String getTestGroupsAsString() {
@@ -475,55 +451,29 @@ private String getTestGroupsAsString() {
String testGroupsAsString = "";
if (testGroups != null) {
testGroupsAsString = "(" +
- testGroups.stream().map(Object::toString).collect(Collectors.joining(",")) +
- ")";
+ testGroups.stream().map(Object::toString).collect(Collectors.joining(",")) +
+ ")";
}
return testGroupsAsString;
}
private J.ClassDeclaration maybeAddTearDownMethodBody(J.ClassDeclaration classDecl, ExecutionContext ctx) {
String testGroupsAsString = (getTestGroupsAsString().isEmpty()) ? tearDownMethodAnnotationParameters : getTestGroupsAsString();
- return maybeAddMethodWithAnnotation(classDecl, ctx, "tearDownStaticMocks",
- tearDownMethodAnnotationSignature,
- tearDownMethodAnnotation,
- additionalClasspathResource, tearDownImportToAdd, testGroupsAsString);
+ return maybeAddMethodWithAnnotation(this, classDecl, ctx, "tearDownStaticMocks",
+ tearDownMethodAnnotationSignature,
+ tearDownMethodAnnotation,
+ additionalClasspathResource, tearDownImportToAdd, testGroupsAsString);
}
- private J.ClassDeclaration maybeAddMethodWithAnnotation(J.ClassDeclaration classDecl, ExecutionContext ctx,
- String methodName, String methodAnnotationSignature,
- String methodAnnotationToAdd,
- String additionalClasspathResource, String importToAdd,
- String methodAnnotationParameters) {
- if (hasMethodWithAnnotation(classDecl, new AnnotationMatcher(methodAnnotationSignature))) {
- return classDecl;
- }
-
- J.MethodDeclaration firstTestMethod = getFirstTestMethod(
- classDecl.getBody().getStatements().stream().filter(J.MethodDeclaration.class::isInstance)
- .map(J.MethodDeclaration.class::cast).collect(Collectors.toList()));
-
- maybeAddImport(importToAdd);
- return JavaTemplate.builder(methodAnnotationToAdd + methodAnnotationParameters + " void " + methodName + "() {}")
- .contextSensitive()
- .javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, additionalClasspathResource))
- .imports(importToAdd)
- .build()
- .apply(
- new Cursor(getCursor().getParentOrThrow(), classDecl),
- (firstTestMethod != null) ?
- firstTestMethod.getCoordinates().before() :
- classDecl.getBody().getCoordinates().lastStatement()
- );
- }
private J.MethodInvocation modifyWhenMethodInvocation(J.MethodInvocation whenMethod) {
List methodArguments = whenMethod.getArguments();
List staticMethodInvocationsInArguments = methodArguments.stream()
- .filter(J.MethodInvocation.class::isInstance).map(J.MethodInvocation.class::cast)
- .filter(methodInvocation -> !MOCKITO_STATIC_METHOD_MATCHER.matches(methodInvocation))
- .filter(methodInvocation -> methodInvocation.getMethodType() != null)
- .filter(methodInvocation -> methodInvocation.getMethodType().hasFlags(Flag.Static))
- .collect(Collectors.toList());
+ .filter(J.MethodInvocation.class::isInstance).map(J.MethodInvocation.class::cast)
+ .filter(methodInvocation -> !MOCKITO_STATIC_METHOD_MATCHER.matches(methodInvocation))
+ .filter(methodInvocation -> methodInvocation.getMethodType() != null)
+ .filter(methodInvocation -> methodInvocation.getMethodType().hasFlags(Flag.Static))
+ .collect(Collectors.toList());
if (staticMethodInvocationsInArguments.size() == 1) {
J.MethodInvocation staticMI = staticMethodInvocationsInArguments.get(0);
Expression lambdaInvocation;
@@ -535,19 +485,19 @@ private J.MethodInvocation modifyWhenMethodInvocation(J.MethodInvocation whenMet
return whenMethod;
}
if (staticMI.getArguments().stream().map(Expression::getType)
- .noneMatch(Objects::nonNull)) {
+ .noneMatch(Objects::nonNull)) {
// If the method invocation has no arguments
lambdaInvocation = JavaTemplate.builder(declaringClassName + "::" + staticMI.getSimpleName())
- .contextSensitive()
- .build()
- .apply(new Cursor(getCursor(), staticMI), staticMI.getCoordinates().replace());
+ .contextSensitive()
+ .build()
+ .apply(new Cursor(getCursor(), staticMI), staticMI.getCoordinates().replace());
} else {
JavaType.Method methodType = staticMI.getMethodType();
if (methodType != null) {
lambdaInvocation = JavaTemplate.builder("() -> #{any()}")
- .contextSensitive()
- .build()
- .apply(new Cursor(getCursor(), staticMI), staticMI.getCoordinates().replace(), staticMI);
+ .contextSensitive()
+ .build()
+ .apply(new Cursor(getCursor(), staticMI), staticMI.getCoordinates().replace(), staticMI);
} else {
// do nothing
lambdaInvocation = staticMI;
@@ -572,18 +522,18 @@ private J.MethodInvocation modifyWhenMethodInvocation(J.MethodInvocation whenMet
private J.@Nullable Identifier getFieldIdentifier(String fieldName) {
return getMockedTypesFields().keySet().stream()
- .filter(identifier -> identifier.getSimpleName().equals(fieldName)).findFirst()
- .orElseGet(() -> {
- J.ClassDeclaration cd = getCursor().dropParentUntil(J.ClassDeclaration.class::isInstance).getValue();
- return cd.getBody().getStatements().stream()
- .filter(J.VariableDeclarations.class::isInstance)
- .map(variableDeclarations -> ((J.VariableDeclarations) variableDeclarations).getVariables())
- .flatMap(Collection::stream)
- .filter(namedVariable -> namedVariable.getSimpleName().equals(fieldName))
- .map(J.VariableDeclarations.NamedVariable::getName)
- .findFirst()
- .orElse(null);
- });
+ .filter(identifier -> identifier.getSimpleName().equals(fieldName)).findFirst()
+ .orElseGet(() -> {
+ J.ClassDeclaration cd = getCursor().dropParentUntil(J.ClassDeclaration.class::isInstance).getValue();
+ return cd.getBody().getStatements().stream()
+ .filter(J.VariableDeclarations.class::isInstance)
+ .map(variableDeclarations -> ((J.VariableDeclarations) variableDeclarations).getVariables())
+ .flatMap(Collection::stream)
+ .filter(namedVariable -> namedVariable.getSimpleName().equals(fieldName))
+ .map(J.VariableDeclarations.NamedVariable::getName)
+ .findFirst()
+ .orElse(null);
+ });
}
}
}
diff --git a/src/main/resources/META-INF/rewrite/classpath/mockito-core-5.13.0.jar b/src/main/resources/META-INF/rewrite/classpath/mockito-core-5.13.0.jar
new file mode 100644
index 000000000..986e38d5c
Binary files /dev/null and b/src/main/resources/META-INF/rewrite/classpath/mockito-core-5.13.0.jar differ
diff --git a/src/main/resources/META-INF/rewrite/jmockit.yml b/src/main/resources/META-INF/rewrite/jmockit.yml
index a15136715..d1ee335d7 100644
--- a/src/main/resources/META-INF/rewrite/jmockit.yml
+++ b/src/main/resources/META-INF/rewrite/jmockit.yml
@@ -23,6 +23,7 @@ tags:
- jmockit
recipeList:
- org.openrewrite.java.testing.jmockit.JMockitBlockToMockito
+ - org.openrewrite.java.testing.jmockit.JMockitMockUpToMockito
- org.openrewrite.java.testing.jmockit.JMockitAnnotatedArgumentToMockito
- org.openrewrite.java.ChangeType:
oldFullyQualifiedTypeName: mockit.Mocked
diff --git a/src/test/java/org/openrewrite/java/testing/jmockit/JMockitMockUpToMockitoTest.java b/src/test/java/org/openrewrite/java/testing/jmockit/JMockitMockUpToMockitoTest.java
new file mode 100644
index 000000000..474763541
--- /dev/null
+++ b/src/test/java/org/openrewrite/java/testing/jmockit/JMockitMockUpToMockitoTest.java
@@ -0,0 +1,651 @@
+/*
+ * 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 org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+
+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;
+import static org.openrewrite.java.testing.jmockit.JMockitTestUtils.*;
+
+class JMockitMockUpToMockitoTest implements RewriteTest {
+
+ @Override
+ public void defaults(RecipeSpec spec) {
+ setParserSettings(spec, JMOCKIT_DEPENDENCY, JUNIT_4_DEPENDENCY);
+ }
+
+ @DocumentExample
+ @Test
+ void mockUpStaticMethodTest() {
+ //language=java
+ rewriteRun(
+ java(
+ """
+ import mockit.Mock;
+ import mockit.MockUp;
+ import static org.junit.Assert.assertEquals;
+ import org.junit.Test;
+
+ public class MockUpTest {
+ @Test
+ public void test() {
+ new MockUp() {
+
+ @Mock
+ public int staticMethod() {
+ return 1024;
+ }
+
+ @Mock
+ public int staticMethod(int v) {
+ return 128;
+ }
+ };
+ assertEquals(1024, MyClazz.staticMethod());
+ assertEquals(128, MyClazz.staticMethod(0));
+ }
+
+ public static class MyClazz {
+ public static int staticMethod() {
+ return 0;
+ }
+
+ public static int staticMethod(int v) {
+ return 1;
+ }
+ }
+ }
+ """, """
+ import static org.junit.Assert.assertEquals;
+ import static org.mockito.ArgumentMatchers.*;
+ import static org.mockito.Mockito.*;
+
+ import org.junit.Test;
+ import org.mockito.MockedStatic;
+
+ public class MockUpTest {
+ @Test
+ public void test() {
+ try (MockedStatic mockStaticMyClazz = mockStatic(MyClazz.class)) {
+ mockStaticMyClazz.when(() -> MyClazz.staticMethod()).thenAnswer(invocation -> 1024);
+ mockStaticMyClazz.when(() -> MyClazz.staticMethod(anyInt())).thenAnswer(invocation -> 128);
+ assertEquals(1024, MyClazz.staticMethod());
+ assertEquals(128, MyClazz.staticMethod(0));
+ }
+ }
+
+ public static class MyClazz {
+ public static int staticMethod() {
+ return 0;
+ }
+
+ public static int staticMethod(int v) {
+ return 1;
+ }
+ }
+ }
+ """));
+ }
+
+ @Test
+ void mockUpMultipleTest() {
+ //language=java
+ rewriteRun(
+ spec -> spec.afterTypeValidationOptions(TypeValidation.builder().identifiers(false).build()),
+ java(
+ """
+ package com.openrewrite;
+ public static class Foo {
+ public String getMsg() {
+ return "foo";
+ }
+
+ public String getMsg(String echo) {
+ return "foo" + echo;
+ }
+ }
+ """,
+ SourceSpec::skip
+ ),
+ java(
+ """
+ package com.openrewrite;
+ public static class Bar {
+ public String getMsg() {
+ return "bar";
+ }
+
+ public String getMsg(String echo) {
+ return "bar" + echo;
+ }
+ }
+ """,
+ SourceSpec::skip
+ ),
+ java(
+ """
+ import com.openrewrite.Foo;
+ import com.openrewrite.Bar;
+ import org.junit.Test;
+ import mockit.Mock;
+ import mockit.MockUp;
+ import static org.junit.Assert.assertEquals;
+
+ public class MockUpTest {
+ @Test
+ public void test() {
+ new MockUp() {
+ @Mock
+ public String getMsg() {
+ return "FOO";
+ }
+ @Mock
+ public String getMsg(String echo) {
+ return "FOO" + echo;
+ }
+ };
+ new MockUp() {
+ @Mock
+ public String getMsg() {
+ return "BAR";
+ }
+ @Mock
+ public String getMsg(String echo) {
+ return "BAR" + echo;
+ }
+ };
+ assertEquals("FOO", new Foo().getMsg());
+ assertEquals("FOOecho", new Foo().getMsg("echo"));
+ assertEquals("BAR", new Bar().getMsg());
+ assertEquals("BARecho", new Bar().getMsg("echo"));
+ }
+ }
+ """, """
+ import com.openrewrite.Foo;
+ import com.openrewrite.Bar;
+ import org.junit.Test;
+ import org.mockito.MockedConstruction;
+ import static org.junit.Assert.assertEquals;
+ import static org.mockito.AdditionalAnswers.delegatesTo;
+ import static org.mockito.Answers.CALLS_REAL_METHODS;
+ import static org.mockito.ArgumentMatchers.*;
+ import static org.mockito.Mockito.*;
+
+ public class MockUpTest {
+ @Test
+ public void test() {
+ Foo mockFoo = mock(Foo.class, CALLS_REAL_METHODS);
+ doAnswer(invocation -> "FOO").when(mockFoo).getMsg();
+ doAnswer(invocation -> {
+ String echo = invocation.getArgument(0);
+ return "FOO" + echo;
+ }).when(mockFoo).getMsg(nullable(String.class));
+ Bar mockBar = mock(Bar.class, CALLS_REAL_METHODS);
+ doAnswer(invocation -> "BAR").when(mockBar).getMsg();
+ doAnswer(invocation -> {
+ String echo = invocation.getArgument(0);
+ return "BAR" + echo;
+ }).when(mockBar).getMsg(nullable(String.class));
+ try (MockedConstruction mockConsFoo = mockConstructionWithAnswer(Foo.class, delegatesTo(mockFoo));MockedConstruction mockConsBar = mockConstructionWithAnswer(Bar.class, delegatesTo(mockBar))) {
+ assertEquals("FOO", new Foo().getMsg());
+ assertEquals("FOOecho", new Foo().getMsg("echo"));
+ assertEquals("BAR", new Bar().getMsg());
+ assertEquals("BARecho", new Bar().getMsg("echo"));
+ }
+ }
+ }
+ """)
+ );
+ }
+
+ @Test
+ void mockUpInnerStatementTest() {
+ //language=java
+ rewriteRun(
+ java(
+ """
+ import mockit.Mock;
+ import mockit.MockUp;
+
+ import org.junit.Test;
+ import static org.junit.Assert.assertEquals;
+
+ public class MockUpTest {
+ @Test
+ public void test() {
+ new MockUp() {
+ final String msg = "newMsg";
+
+ @Mock
+ public String getMsg() {
+ return msg;
+ }
+ };
+
+ // Should ignore the newClass statement
+ new Runnable() {
+ @Override
+ public void run() {
+ System.out.println("run");
+ }
+ };
+ assertEquals("newMsg", new MyClazz().getMsg());
+ }
+
+ public static class MyClazz {
+ public String getMsg() {
+ return "msg";
+ }
+ }
+ }
+ """, """
+ import org.junit.Test;
+ import org.mockito.MockedConstruction;
+
+ import static org.junit.Assert.assertEquals;
+ import static org.mockito.AdditionalAnswers.delegatesTo;
+ import static org.mockito.Answers.CALLS_REAL_METHODS;
+ import static org.mockito.Mockito.*;
+
+ public class MockUpTest {
+ @Test
+ public void test() {
+ final String msg = "newMsg";
+ MyClazz mockMyClazz = mock(MyClazz.class, CALLS_REAL_METHODS);
+ doAnswer(invocation -> msg).when(mockMyClazz).getMsg();
+ try (MockedConstruction mockConsMyClazz = mockConstructionWithAnswer(MyClazz.class, delegatesTo(mockMyClazz))) {
+
+ // Should ignore the newClass statement
+ new Runnable() {
+ @Override
+ public void run() {
+ System.out.println("run");
+ }
+ };
+ assertEquals("newMsg", new MyClazz().getMsg());
+ }
+ }
+
+ public static class MyClazz {
+ public String getMsg() {
+ return "msg";
+ }
+ }
+ }
+ """));
+ }
+
+ @Test
+ void mockUpVoidTest() {
+ //language=java
+ rewriteRun(
+ java(
+ """
+ import mockit.Mock;
+ import mockit.MockUp;
+ import static org.junit.Assert.assertEquals;
+ import org.junit.Test;
+
+ public class MockUpTest {
+ @Test
+ public void test() {
+ new MockUp() {
+ @Mock
+ public void changeMsg() {
+ MockUpClass.Save.msg = "mockMsg";
+ }
+
+ @Mock
+ public void changeText(String text) {
+ MockUpClass.Save.text = "mockText";
+ }
+ };
+
+ assertEquals("mockMsg", new MockUpClass().getMsg());
+ assertEquals("mockText", new MockUpClass().getText());
+ }
+
+ public static class MockUpClass {
+ public static class Save {
+ public static String msg = "msg";
+ public static String text = "text";
+ }
+
+ public final String getMsg() {
+ changeMsg();
+ return Save.msg;
+ }
+
+ public void changeMsg() {
+ Save.msg = "newMsg";
+ }
+
+ public String getText() {
+ changeText("newText");
+ return Save.text;
+ }
+
+ public static void changeText(String text) {
+ Save.text = text;
+ }
+ }
+ }
+ """,
+ """
+ import static org.junit.Assert.assertEquals;
+ import static org.mockito.AdditionalAnswers.delegatesTo;
+ import static org.mockito.Answers.CALLS_REAL_METHODS;
+ import static org.mockito.ArgumentMatchers.*;
+ import static org.mockito.Mockito.*;
+
+ import org.junit.Test;
+ import org.mockito.MockedConstruction;
+ import org.mockito.MockedStatic;
+
+ public class MockUpTest {
+ @Test
+ public void test() {
+ MockUpClass mockMockUpClass = mock(MockUpClass.class, CALLS_REAL_METHODS);
+ doAnswer(invocation -> {
+ MockUpClass.Save.msg = "mockMsg";
+ return null;
+ }).when(mockMockUpClass).changeMsg();
+ try (MockedStatic mockStaticMockUpClass = mockStatic(MockUpClass.class);MockedConstruction mockConsMockUpClass = mockConstructionWithAnswer(MockUpClass.class, delegatesTo(mockMockUpClass))) {
+ mockStaticMockUpClass.when(() -> MockUpClass.changeText(nullable(String.class))).thenAnswer(invocation -> {
+ String text = invocation.getArgument(0);
+ MockUpClass.Save.text = "mockText";
+ return null;
+ });
+
+ assertEquals("mockMsg", new MockUpClass().getMsg());
+ assertEquals("mockText", new MockUpClass().getText());
+ }
+ }
+
+ public static class MockUpClass {
+ public static class Save {
+ public static String msg = "msg";
+ public static String text = "text";
+ }
+
+ public final String getMsg() {
+ changeMsg();
+ return Save.msg;
+ }
+
+ public void changeMsg() {
+ Save.msg = "newMsg";
+ }
+
+ public String getText() {
+ changeText("newText");
+ return Save.text;
+ }
+
+ public static void changeText(String text) {
+ Save.text = text;
+ }
+ }
+ }
+ """));
+ }
+
+ @Test
+ void mockUpAtSetUpWithoutTearDownTest() {
+ rewriteRun(
+ java(
+ """
+ import org.junit.Before;
+ import org.junit.Test;
+ import mockit.Mock;
+ import mockit.MockUp;
+ import static org.junit.Assert.assertEquals;
+
+ public class MockUpTest {
+ @Before
+ public void setUp() {
+ new MockUp() {
+ @Mock
+ public String getMsg() {
+ return "mockMsg";
+ }
+ };
+ }
+
+ @Test
+ public void test() {
+ assertEquals("mockMsg", new MyClazz().getMsg());
+ }
+
+ public static class MyClazz {
+ public String getMsg() {
+ return "msg";
+ }
+ }
+ }
+ """,
+ """
+ import org.junit.After;
+ import org.junit.Before;
+ import org.junit.Test;
+ import org.mockito.MockedConstruction;
+ import static org.junit.Assert.assertEquals;
+ import static org.mockito.AdditionalAnswers.delegatesTo;
+ import static org.mockito.Answers.CALLS_REAL_METHODS;
+ import static org.mockito.Mockito.*;
+
+ public class MockUpTest {
+ private MockedConstruction mockConsMyClazz;
+
+ @Before
+ public void setUp() {
+ MyClazz mockMyClazz = mock(MyClazz.class, CALLS_REAL_METHODS);
+ doAnswer(invocation -> "mockMsg").when(mockMyClazz).getMsg();
+ mockConsMyClazz = mockConstructionWithAnswer(MyClazz.class, delegatesTo(mockMyClazz));
+ }
+
+ @After
+ public void tearDown() {
+ mockConsMyClazz.closeOnDemand();
+ }
+
+ @Test
+ public void test() {
+ assertEquals("mockMsg", new MyClazz().getMsg());
+ }
+
+ public static class MyClazz {
+ public String getMsg() {
+ return "msg";
+ }
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void mockUpAtSetUpWithTearDownTest() {
+ rewriteRun(
+ java(
+ """
+ import org.junit.Before;
+ import org.junit.After;
+ import org.junit.Test;
+ import mockit.Mock;
+ import mockit.MockUp;
+ import static org.junit.Assert.assertEquals;
+
+ public class MockUpTest {
+ @Before
+ public void setUp() {
+ new MockUp() {
+ @Mock
+ public String getMsg() {
+ return "mockMsg";
+ }
+
+ @Mock
+ public String getStaticMsg() {
+ return "mockStaticMsg";
+ }
+ };
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ @Test
+ public void test() {
+ assertEquals("mockMsg", new MyClazz().getMsg());
+ assertEquals("mockStaticMsg", MyClazz.getStaticMsg());
+ }
+
+ public static class MyClazz {
+ public String getMsg() {
+ return "msg";
+ }
+
+ public static String getStaticMsg() {
+ return "staticMsg";
+ }
+ }
+ }
+ """,
+ """
+ import org.junit.Before;
+ import org.junit.After;
+ import org.junit.Test;
+ import org.mockito.MockedConstruction;
+ import org.mockito.MockedStatic;
+ import static org.junit.Assert.assertEquals;
+ import static org.mockito.AdditionalAnswers.delegatesTo;
+ import static org.mockito.Answers.CALLS_REAL_METHODS;
+ import static org.mockito.Mockito.*;
+
+ public class MockUpTest {
+ private MockedConstruction mockConsMyClazz;
+ private MockedStatic mockStaticMyClazz;
+
+ @Before
+ public void setUp() {
+ MyClazz mockMyClazz = mock(MyClazz.class, CALLS_REAL_METHODS);
+ doAnswer(invocation -> "mockMsg").when(mockMyClazz).getMsg();
+ mockConsMyClazz = mockConstructionWithAnswer(MyClazz.class, delegatesTo(mockMyClazz));
+ mockStaticMyClazz = mockStatic(MyClazz.class);
+ mockStaticMyClazz.when(() -> MyClazz.getStaticMsg()).thenAnswer(invocation -> "mockStaticMsg");
+ }
+
+ @After
+ public void tearDown() {
+ mockConsMyClazz.closeOnDemand();
+ mockStaticMyClazz.closeOnDemand();
+ }
+
+ @Test
+ public void test() {
+ assertEquals("mockMsg", new MyClazz().getMsg());
+ assertEquals("mockStaticMsg", MyClazz.getStaticMsg());
+ }
+
+ public static class MyClazz {
+ public String getMsg() {
+ return "msg";
+ }
+
+ public static String getStaticMsg() {
+ return "staticMsg";
+ }
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void mockUpWithParamsTest() {
+ rewriteRun(
+ java(
+ """
+ import mockit.Mock;
+ import mockit.MockUp;
+ import org.junit.Test;
+
+ import static org.junit.Assert.assertEquals;
+
+ public class MockUpTest {
+ @Test
+ public void init() {
+ new MockUp() {
+ @Mock
+ public String getMsg(String foo, String bar, String unused) {
+ return foo + bar;
+ }
+ };
+ assertEquals("foobar", new MyClazz().getMsg("foo", "bar", "unused"));
+ }
+
+ public static class MyClazz {
+ public String getMsg(String foo, String bar, String unused) {
+ return "msg";
+ }
+ }
+ }
+ """,
+ """
+ import org.junit.Test;
+ import org.mockito.MockedConstruction;
+
+ import static org.junit.Assert.assertEquals;
+ import static org.mockito.AdditionalAnswers.delegatesTo;
+ import static org.mockito.Answers.CALLS_REAL_METHODS;
+ import static org.mockito.ArgumentMatchers.*;
+ import static org.mockito.Mockito.*;
+
+ public class MockUpTest {
+ @Test
+ public void init() {
+ MyClazz mockMyClazz = mock(MyClazz.class, CALLS_REAL_METHODS);
+ doAnswer(invocation -> {
+ String foo = invocation.getArgument(0);
+ String bar = invocation.getArgument(1);
+ return foo + bar;
+ }).when(mockMyClazz).getMsg(nullable(String.class), nullable(String.class), nullable(String.class));
+ try (MockedConstruction mockConsMyClazz = mockConstructionWithAnswer(MyClazz.class, delegatesTo(mockMyClazz))) {
+ assertEquals("foobar", new MyClazz().getMsg("foo", "bar", "unused"));
+ }
+ }
+
+ public static class MyClazz {
+ public String getMsg(String foo, String bar, String unused) {
+ return "msg";
+ }
+ }
+ }
+ """
+ )
+ );
+ }
+}
diff --git a/src/test/java/org/openrewrite/java/testing/jmockit/JMockitNonStrictExpectationsToMockitoTest.java b/src/test/java/org/openrewrite/java/testing/jmockit/JMockitNonStrictExpectationsToMockitoTest.java
index bc186bb48..75227e326 100644
--- a/src/test/java/org/openrewrite/java/testing/jmockit/JMockitNonStrictExpectationsToMockitoTest.java
+++ b/src/test/java/org/openrewrite/java/testing/jmockit/JMockitNonStrictExpectationsToMockitoTest.java
@@ -21,12 +21,9 @@
import org.openrewrite.test.RewriteTest;
import static org.openrewrite.java.Assertions.java;
-import static org.openrewrite.java.testing.jmockit.JMockitTestUtils.MOCKITO_CORE_DEPENDENCY;
-import static org.openrewrite.java.testing.jmockit.JMockitTestUtils.setParserSettings;
+import static org.openrewrite.java.testing.jmockit.JMockitTestUtils.*;
class JMockitNonStrictExpectationsToMockitoTest implements RewriteTest {
-
- private static final String JUNIT_4_DEPENDENCY = "junit-4.13.2";
private static final String LEGACY_JMOCKIT_DEPENDENCY = "jmockit-1.22";
@Override
diff --git a/src/test/java/org/openrewrite/java/testing/jmockit/JMockitTestUtils.java b/src/test/java/org/openrewrite/java/testing/jmockit/JMockitTestUtils.java
index c66d3fbb1..9c2ac5f31 100644
--- a/src/test/java/org/openrewrite/java/testing/jmockit/JMockitTestUtils.java
+++ b/src/test/java/org/openrewrite/java/testing/jmockit/JMockitTestUtils.java
@@ -24,6 +24,7 @@ public class JMockitTestUtils {
static final String MOCKITO_CORE_DEPENDENCY = "mockito-core-3.12";
static final String JUNIT_5_JUPITER_DEPENDENCY = "junit-jupiter-api-5.9";
+ static final String JUNIT_4_DEPENDENCY = "junit-4.13.2";
static final String JMOCKIT_DEPENDENCY = "jmockit-1.49";
static final String MOCKITO_JUPITER_DEPENDENCY = "mockito-junit-jupiter-3.12";