diff --git a/src/main/java/org/openrewrite/java/testing/mockito/VerifyZeroToNoMoreInteractions.java b/src/main/java/org/openrewrite/java/testing/mockito/VerifyZeroToNoMoreInteractions.java
new file mode 100644
index 000000000..2315e88ba
--- /dev/null
+++ b/src/main/java/org/openrewrite/java/testing/mockito/VerifyZeroToNoMoreInteractions.java
@@ -0,0 +1,84 @@
+/*
+ * 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.*;
+import org.openrewrite.java.ChangeMethodName;
+import org.openrewrite.java.JavaIsoVisitor;
+import org.openrewrite.java.MethodMatcher;
+import org.openrewrite.java.dependencies.DependencyInsight;
+import org.openrewrite.java.search.UsesMethod;
+import org.openrewrite.java.tree.J;
+
+import java.util.concurrent.atomic.AtomicBoolean;
+
+public class VerifyZeroToNoMoreInteractions extends ScanningRecipe {
+
+ private static final String VERIFY_ZERO_INTERACTIONS = "org.mockito.Mockito verifyZeroInteractions(..)";
+ private static final MethodMatcher ASSERT_INSTANCE_OF_MATCHER = new MethodMatcher(VERIFY_ZERO_INTERACTIONS, true);
+
+ @Override
+ public String getDisplayName() {
+ return "Replace `verifyZeroInteractions() to `verifyNoMoreInteractions()";
+ }
+
+ @Override
+ public String getDescription() {
+ return "Replaces `verifyZeroInteractions()` with `verifyNoMoreInteractions()` in Mockito tests when migration when using a Mockito version < 3.x.";
+ }
+
+ @Override
+ public AtomicBoolean getInitialValue(final ExecutionContext ctx) {
+ return new AtomicBoolean(false);
+ }
+
+ @Override
+ public TreeVisitor, ExecutionContext> getScanner(AtomicBoolean usingOlderMockito) {
+ TreeVisitor, ExecutionContext> div = new DependencyInsight("org.mockito", "mockito-*", "[1.0,3.0)", null).getVisitor();
+ return new TreeVisitor() {
+ @Override
+ public @Nullable Tree visit(@Nullable Tree tree, ExecutionContext ctx) {
+ if (!usingOlderMockito.get() && div.visit(tree, ctx) != tree) {
+ usingOlderMockito.set(true);
+ }
+ return tree;
+ }
+ };
+ }
+
+ @Override
+ public TreeVisitor, ExecutionContext> getVisitor(AtomicBoolean usingOlderMockito) {
+ return Preconditions.check(usingOlderMockito.get(),
+ Preconditions.check(new UsesMethod<>(ASSERT_INSTANCE_OF_MATCHER), new JavaIsoVisitor() {
+ @Override
+ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
+ J.MethodInvocation md = super.visitMethodInvocation(method, ctx);
+
+ if (!ASSERT_INSTANCE_OF_MATCHER.matches(md)) {
+ return md;
+ }
+
+ maybeAddImport("org.mockito.Mockito", "verifyNoMoreInteractions");
+ maybeRemoveImport("org.mockito.Mockito.verifyZeroInteractions");
+
+ ChangeMethodName changeMethodName = new ChangeMethodName(VERIFY_ZERO_INTERACTIONS, "verifyNoMoreInteractions", false, false);
+ return (J.MethodInvocation) changeMethodName.getVisitor().visitNonNull(md, ctx);
+ }
+ })
+ );
+ }
+}
diff --git a/src/main/resources/META-INF/rewrite/mockito.yml b/src/main/resources/META-INF/rewrite/mockito.yml
index d1b62eaf2..dc569f5e0 100644
--- a/src/main/resources/META-INF/rewrite/mockito.yml
+++ b/src/main/resources/META-INF/rewrite/mockito.yml
@@ -132,6 +132,7 @@ recipeList:
oldParameterNames:
- mode
- verification
+ - org.openrewrite.java.testing.mockito.VerifyZeroToNoMoreInteractions
- org.openrewrite.java.ChangeMethodName:
methodPattern: org.mockito.Mockito verifyZeroInteractions(..)
newMethodName: verifyNoInteractions
diff --git a/src/test/java/org/openrewrite/java/testing/mockito/VerifyZeroToNoMoreInteractionsTest.java b/src/test/java/org/openrewrite/java/testing/mockito/VerifyZeroToNoMoreInteractionsTest.java
new file mode 100644
index 000000000..6b342323f
--- /dev/null
+++ b/src/test/java/org/openrewrite/java/testing/mockito/VerifyZeroToNoMoreInteractionsTest.java
@@ -0,0 +1,214 @@
+/*
+ * 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.intellij.lang.annotations.Language;
+import org.junit.jupiter.api.Test;
+import org.openrewrite.DocumentExample;
+import org.openrewrite.InMemoryExecutionContext;
+import org.openrewrite.java.JavaParser;
+import org.openrewrite.test.RecipeSpec;
+import org.openrewrite.test.RewriteTest;
+
+import static org.openrewrite.java.Assertions.java;
+import static org.openrewrite.maven.Assertions.pomXml;
+
+class VerifyZeroToNoMoreInteractionsTest implements RewriteTest {
+
+ @Language("xml")
+ private static final String POM_XML_WITH_MOCKITO_2 = """
+
+ 4.0.0
+ bla.bla
+ bla-bla
+ 1.0.0
+
+
+ org.mockito
+ mockito-core
+ 2.17.0
+ test
+
+
+
+ """;
+
+ @Override
+ public void defaults(RecipeSpec spec) {
+ spec
+ .parser(JavaParser.fromJavaVersion()
+ .classpathFromResources(new InMemoryExecutionContext(), "mockito-core-3", "mockito-junit-jupiter-3"))
+ .recipe(new VerifyZeroToNoMoreInteractions());
+ }
+
+ @Test
+ @DocumentExample
+ void shouldReplaceToNoMoreInteractions() {
+ //language=java
+ rewriteRun(
+ pomXml(POM_XML_WITH_MOCKITO_2),
+ java(
+ """
+ import static org.mockito.Mockito.verifyZeroInteractions;
+
+ class MyTest {
+ void test() {
+ verifyZeroInteractions(System.out);
+ }
+ }
+ """,
+ """
+ import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+ class MyTest {
+ void test() {
+ verifyNoMoreInteractions(System.out);
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void shouldNotReplaceToNoMoreInteractionsForImportOnly() {
+ //language=java
+ rewriteRun(
+ pomXml(POM_XML_WITH_MOCKITO_2),
+ java(
+ """
+ import static org.mockito.Mockito.verifyZeroInteractions;
+
+ class MyTest {}
+ """
+ )
+ );
+ }
+
+ @Test
+ void doesNotConvertAnyOtherMethods() {
+ rewriteRun(
+ pomXml(POM_XML_WITH_MOCKITO_2),
+ // language=java
+ java(
+ """
+ import org.mockito.junit.jupiter.MockitoExtension;
+ import org.mockito.Mock;
+ import static org.mockito.Mockito.verifyZeroInteractions;
+ import static org.mockito.Mockito.verify;
+
+ class MyTest {
+ @Mock
+ Object myObject;
+
+ void test() {
+ verifyZeroInteractions(System.out);
+ verify(myObject);
+ }
+ }
+ """,
+ """
+ import org.mockito.junit.jupiter.MockitoExtension;
+ import org.mockito.Mock;
+ import static org.mockito.Mockito.verifyNoMoreInteractions;
+ import static org.mockito.Mockito.verify;
+
+ class MyTest {
+ @Mock
+ Object myObject;
+
+ void test() {
+ verifyNoMoreInteractions(System.out);
+ verify(myObject);
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void doesConvertNestedMethodInvocations() {
+ rewriteRun(
+ pomXml(POM_XML_WITH_MOCKITO_2),
+ // language=java
+ java(
+ """
+ import java.util.function.Consumer;
+
+ import static org.mockito.Mockito.verifyZeroInteractions;
+
+ class MyTest {
+ void test() {
+ Runnable f = () -> verifyZeroInteractions(System.out);
+ f.run();
+ }
+ }
+ """,
+ """
+ import java.util.function.Consumer;
+
+ import static org.mockito.Mockito.verifyNoMoreInteractions;
+
+ class MyTest {
+ void test() {
+ Runnable f = () -> verifyNoMoreInteractions(System.out);
+ f.run();
+ }
+ }
+ """
+ )
+ );
+ }
+
+ @Test
+ void shouldNotRunOnNewerMockito3OrHigher() {
+ rewriteRun(
+ //language=xml
+ pomXml(
+ """
+
+ 4.0.0
+ bla.bla
+ bla-bla
+ 1.0.0
+
+
+ org.mockito
+ mockito-core
+ 3.0.0
+ test
+
+
+
+ """),
+ //language=java
+ java(
+ """
+ import org.mockito.junit.jupiter.MockitoExtension;
+
+ import static org.mockito.Mockito.verifyZeroInteractions;
+
+ class MyTest {
+ void test() {
+ verifyZeroInteractions(System.out);
+ }
+ }
+ """
+ )
+ );
+ }
+}