From 288c9ef266076d82a11b3907d47f713290a51bfe Mon Sep 17 00:00:00 2001 From: Shivani Sharma Date: Thu, 20 Jun 2024 19:28:43 +1000 Subject: [PATCH] Jmockit Expectations with no times or result should be transformed to Mockito verify statement (#530) * Ensure Jmockit expectations with no times or result transform to a mockito verify * Minor polish to text blocks * Make times, minTimes, maxTimes more flexible as it was originally so even if someone mistakenly puts times and minTimes together, it still migrates without issue --------- Co-authored-by: Tim te Beek --- .../jmockit/ExpectationsBlockRewriter.java | 43 +- .../JMockitExpectationsToMockitoTest.java | 383 +++++++++++++++--- 2 files changed, 352 insertions(+), 74 deletions(-) diff --git a/src/main/java/org/openrewrite/java/testing/jmockit/ExpectationsBlockRewriter.java b/src/main/java/org/openrewrite/java/testing/jmockit/ExpectationsBlockRewriter.java index c45b0b798..ae2dce1ee 100644 --- a/src/main/java/org/openrewrite/java/testing/jmockit/ExpectationsBlockRewriter.java +++ b/src/main/java/org/openrewrite/java/testing/jmockit/ExpectationsBlockRewriter.java @@ -17,6 +17,7 @@ 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; @@ -118,22 +119,31 @@ private void rewriteMethodBody(List expectationStatements) { return; } J.MethodInvocation invocation = (J.MethodInvocation) expectationStatements.get(0); - if (!mockInvocationResults.getResults().isEmpty()) { + boolean hasExpectationsResults = !mockInvocationResults.getResults().isEmpty(); + if (hasExpectationsResults) { // 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(); } + + boolean hasTimes = false; if (mockInvocationResults.getTimes() != null) { + hasTimes = true; writeMethodVerification(invocation, mockInvocationResults.getTimes(), "times"); } if (mockInvocationResults.getMinTimes() != null) { + hasTimes = true; writeMethodVerification(invocation, mockInvocationResults.getMinTimes(), "atLeast"); } if (mockInvocationResults.getMaxTimes() != null) { + hasTimes = true; writeMethodVerification(invocation, mockInvocationResults.getMaxTimes(), "atMost"); } + if (!hasExpectationsResults && !hasTimes) { + writeMethodVerification(invocation, null, null); + } } private void rewriteExpectationResult(List results, J.MethodInvocation invocation) { @@ -182,18 +192,22 @@ private void removeExpectationsStatement() { methodBody.getStatements().get(bodyStatementIndex + numStatementsAdded).getCoordinates().after(); } - private void writeMethodVerification(J.MethodInvocation invocation, Expression times, String verificationMode) { + private void writeMethodVerification(J.MethodInvocation invocation, @Nullable Expression times, @Nullable String verificationMode) { String fqn = getInvocationSelectFullyQualifiedClassName(invocation); if (fqn == 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); + if (verificationMode != null) { + 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(), fqn, verificationMode, templateParams); @@ -251,17 +265,20 @@ 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, String fqn, @Nullable String verificationMode, List templateParams) { + StringBuilder templateBuilder = new StringBuilder("verify(#{any(" + fqn + ")}"); // verify(object + if (verificationMode != null) { + templateBuilder.append(", ").append(verificationMode).append("(#{any(int)})"); // verify(object, times(2) + } + templateBuilder.append(").#{}("); // verify(object, times(2)).method( + if (arguments.isEmpty()) { - return "verify(#{any(" + fqn + ")}, " - + verificationMode - + "(#{any(int)})).#{}();"; + templateBuilder.append(");"); // verify(object, times(2)).method(); + return templateBuilder.toString(); } - StringBuilder templateBuilder = new StringBuilder("verify(#{any(" + fqn + ")}, " - + verificationMode - + "(#{any(int)})).#{}("); + boolean hasArgument = false; - for (Expression argument : arguments) { + for (Expression argument : arguments) { // verify(object, times(2).method(anyLong(), any Int() if (argument instanceof J.Empty) { continue; } else if (argument instanceof J.Literal) { @@ -276,7 +293,7 @@ private static String getVerifyTemplate(List arguments, String fqn, if (hasArgument) { templateBuilder.delete(templateBuilder.length() - 2, templateBuilder.length()); } - templateBuilder.append(");"); + templateBuilder.append(");"); // verify(object, times(2).method(anyLong(), any Int()); return templateBuilder.toString(); } 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..69f4627fe 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( @@ -71,6 +71,55 @@ 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 + Object myObject; + + 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 { @@ -79,6 +128,7 @@ class MyTest { void test() { myObject.wait(10L, 10); + verify(myObject).wait(); } } """ @@ -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; @@ -529,7 +579,7 @@ public String getSomeOtherField(Object input) { """ ), java( - """ + """ import java.util.ArrayList; import java.util.List; @@ -544,7 +594,7 @@ public String getSomeOtherField(Object input) { class MyTest { @Mocked MyObject myObject; - + void test() { new Expectations() {{ myObject.getSomeField((List) any); @@ -572,7 +622,7 @@ void test() { 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 @@ -601,7 +718,7 @@ public String getSomeField(String s, String s2, String s3, long l1) { """ ), java( - """ + """ import java.util.ArrayList; import java.util.List; @@ -616,7 +733,7 @@ public String getSomeField(String s, String s2, String s3, long l1) { class MyTest { @Mocked MyObject myObject; - + void test() { String bazz = "bazz"; new Expectations() {{ @@ -642,7 +759,7 @@ void test() { class MyTest { @Mock MyObject myObject; - + void test() { String bazz = "bazz"; when(myObject.getSomeField(eq("foo"), anyString(), eq(bazz), eq(10L))).thenReturn(null); @@ -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,11 @@ 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 +864,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 +885,7 @@ void test() { String b = "b"; result = s; }}; - + assertEquals("s", myObject.getSomeField("foo")); } } @@ -777,7 +894,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 +909,7 @@ void test() { String s = "s"; String b = "b"; when(myObject.getSomeField(anyString())).thenReturn(s); - + assertEquals("s", myObject.getSomeField("foo")); } } @@ -803,6 +920,55 @@ void test() { @Test 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 = 2; + }}; + 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); + verify(myObject, times(2)).wait(anyLong(), anyInt()); + } + } + """ + ) + ); + } + + @Test + void whenMinTimes() { //language=java rewriteRun( java( @@ -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); + } + } + """, + """ + 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, times(2)).wait(anyLong(), anyInt()); + 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()); @@ -943,10 +1203,10 @@ public void doSomething() {} class MyTest { @Mocked Object myObject; - + @Mocked MyObject myOtherObject; - + void test() { new Expectations() {{ myObject.hashCode(); @@ -989,6 +1249,7 @@ void test() { assertNull(myOtherObject.getSomeObjectField()); myObject.wait(10L, 10); assertEquals("foo", myOtherObject.getSomeStringField("bar", 10L)); + verify(myObject).wait(anyLong(), anyInt()); } } """