Skip to content

Commit e2bb0ae

Browse files
Add migration of JMockit NonStrictExpectations blocks as well as JMockit junit 4 runner (#546)
* Add migration of JMockit NonStrictExpectations blocks as well as JMockit junit 4 runner * Add unit tests for NonStrictExpectations, all passing, refactor to reduce code duplication * Add missing copyright clause * Remove @nullable annotation because using javax or open rewrite internal annotation is not recommended as per code review * Add missing nullable annotations --------- Co-authored-by: Tim te Beek <[email protected]>
1 parent 3211368 commit e2bb0ae

File tree

11 files changed

+1211
-76
lines changed

11 files changed

+1211
-76
lines changed

build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ recipeDependencies {
2020
parserClasspath("org.mockito:mockito-all:1.10.19")
2121
parserClasspath("org.mockito:mockito-core:3.+")
2222
parserClasspath("org.jmockit:jmockit:1.49")
23+
parserClasspath("org.jmockit:jmockit:1.22") // last version with NonStrictExpectations
2324
parserClasspath("org.mockito:mockito-junit-jupiter:3.+")
2425
parserClasspath("org.powermock:powermock-api-mockito:1.7.+")
2526
parserClasspath("org.powermock:powermock-core:1.7.+")

src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockRewriter.java

Lines changed: 52 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,15 @@
2929
import java.util.ArrayList;
3030
import java.util.List;
3131

32+
import static org.openrewrite.java.testing.jmockit.JMockitBlockType.NonStrictExpectations;
3233
import static org.openrewrite.java.testing.jmockit.JMockitBlockType.Verifications;
3334

3435
class JMockitBlockRewriter {
3536

3637
private static final String WHEN_TEMPLATE_PREFIX = "when(#{any()}).";
38+
private static final String VERIFY_TEMPLATE_PREFIX = "verify(#{any()}";
39+
private static final String LENIENT_TEMPLATE_PREFIX = "lenient().";
40+
3741
private static final String RETURN_TEMPLATE_PREFIX = "thenReturn(";
3842
private static final String THROW_TEMPLATE_PREFIX = "thenThrow(";
3943
private static final String LITERAL_TEMPLATE_FIELD = "#{}";
@@ -131,6 +135,11 @@ private void rewriteMethodInvocation(List<Statement> statementsToRewrite) {
131135
rewriteResult(invocation, mockInvocationResults.getResults());
132136
}
133137

138+
if (blockType == NonStrictExpectations) {
139+
// no verify for NonStrictExpectations
140+
return;
141+
}
142+
134143
boolean hasTimes = false;
135144
if (mockInvocationResults.getTimes() != null) {
136145
hasTimes = true;
@@ -145,22 +154,19 @@ private void rewriteMethodInvocation(List<Statement> statementsToRewrite) {
145154
rewriteVerify(invocation, mockInvocationResults.getMaxTimes(), "atMost");
146155
}
147156
if (!hasResults && !hasTimes) {
148-
rewriteVerify(invocation, null, null);
157+
rewriteVerify(invocation, null, "");
149158
}
150159
}
151160

152161
private void removeBlock() {
153162
methodBody = JavaTemplate.builder("")
154163
.javaParser(JavaParser.fromJavaVersion())
155164
.build()
156-
.apply(
157-
new Cursor(visitor.getCursor(), methodBody),
158-
nextStatementCoordinates
159-
);
165+
.apply(new Cursor(visitor.getCursor(), methodBody), nextStatementCoordinates);
160166
if (bodyStatementIndex == 0) {
161167
nextStatementCoordinates = methodBody.getCoordinates().firstStatement();
162168
} else {
163-
setNextCoordinatesAfterLastStatementAdded(0);
169+
setNextStatementCoordinates(0);
164170
}
165171
}
166172

@@ -171,17 +177,26 @@ private void rewriteResult(J.MethodInvocation invocation, List<Expression> resul
171177
rewriteFailed = true;
172178
return;
173179
}
174-
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, "when");
175180

176181
List<Object> templateParams = new ArrayList<>();
177182
templateParams.add(invocation);
178183
templateParams.addAll(results);
179-
180-
methodBody = rewriteTemplate(template, templateParams, nextStatementCoordinates);
181-
setNextCoordinatesAfterLastStatementAdded(++numStatementsAdded);
184+
this.rewriteFailed = !rewriteTemplate(template, templateParams, nextStatementCoordinates);
185+
if (!this.rewriteFailed) {
186+
this.rewriteFailed = true;
187+
setNextStatementCoordinates(++numStatementsAdded);
188+
// do this last making sure rewrite worked and specify hasReference=false because framework cannot find static
189+
// reference for when method invocation when lenient is added.
190+
boolean hasReferencesForWhen = true;
191+
if (this.blockType == NonStrictExpectations) {
192+
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, "lenient");
193+
hasReferencesForWhen = false;
194+
}
195+
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, "when", hasReferencesForWhen);
196+
}
182197
}
183198

184-
private void rewriteVerify(J.MethodInvocation invocation, @Nullable Expression times, @Nullable String verificationMode) {
199+
private void rewriteVerify(J.MethodInvocation invocation, @Nullable Expression times, String verificationMode) {
185200
if (invocation.getSelect() == null) {
186201
// cannot write a verification statement for an invocation without a select field
187202
return;
@@ -194,7 +209,6 @@ private void rewriteVerify(J.MethodInvocation invocation, @Nullable Expression t
194209
}
195210
templateParams.add(invocation.getName().getSimpleName());
196211
String verifyTemplate = getVerifyTemplate(invocation.getArguments(), verificationMode, templateParams);
197-
198212
JavaCoordinates verifyCoordinates;
199213
if (this.blockType == Verifications) {
200214
// for Verifications, replace the Verifications block
@@ -203,21 +217,22 @@ private void rewriteVerify(J.MethodInvocation invocation, @Nullable Expression t
203217
// for Expectations put the verify at the end of the method
204218
verifyCoordinates = methodBody.getCoordinates().lastStatement();
205219
}
220+
this.rewriteFailed = !rewriteTemplate(verifyTemplate, templateParams, verifyCoordinates);
221+
if (!this.rewriteFailed) {
222+
if (this.blockType == Verifications) {
223+
setNextStatementCoordinates(++numStatementsAdded); // for Expectations, verify statements added to end of method
224+
}
206225

207-
methodBody = rewriteTemplate(verifyTemplate, templateParams, verifyCoordinates);
208-
if (this.blockType == Verifications) {
209-
setNextCoordinatesAfterLastStatementAdded(++numStatementsAdded);
210-
}
211-
212-
// do this last making sure rewrite worked and specify hasReference=false because in verify case it cannot find
213-
// the static reference in AddImport class, and getSelect() returns not null
214-
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, "verify", false);
215-
if (verificationMode != null) {
216-
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, verificationMode);
226+
// do this last making sure rewrite worked and specify hasReference=false because in verify case framework
227+
// cannot find the static reference
228+
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, "verify", false);
229+
if (!verificationMode.isEmpty()) {
230+
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, verificationMode);
231+
}
217232
}
218233
}
219234

220-
private void setNextCoordinatesAfterLastStatementAdded(int numStatementsAdded) {
235+
private void setNextStatementCoordinates(int numStatementsAdded) {
221236
// the next statement coordinates are directly after the most recently written statement, calculated by
222237
// subtracting the removed jmockit block
223238
int nextStatementIdx = bodyStatementIndex + numStatementsAdded - 1;
@@ -228,23 +243,28 @@ private void setNextCoordinatesAfterLastStatementAdded(int numStatementsAdded) {
228243
}
229244
}
230245

231-
private J.Block rewriteTemplate(String verifyTemplate, List<Object> templateParams, JavaCoordinates
246+
private boolean rewriteTemplate(String template, List<Object> templateParams, JavaCoordinates
232247
rewriteCoords) {
233-
JavaTemplate.Builder builder = JavaTemplate.builder(verifyTemplate)
248+
int numStatementsBefore = methodBody.getStatements().size();
249+
methodBody = JavaTemplate.builder(template)
234250
.javaParser(JavaParser.fromJavaVersion().classpathFromResources(ctx, "mockito-core-3.12"))
235-
.staticImports("org.mockito.Mockito.*");
236-
return builder
251+
.staticImports("org.mockito.Mockito.*")
237252
.build()
238253
.apply(
239254
new Cursor(visitor.getCursor(), methodBody),
240255
rewriteCoords,
241256
templateParams.toArray()
242257
);
258+
return methodBody.getStatements().size() > numStatementsBefore;
243259
}
244260

245-
private static @Nullable String getWhenTemplate(List<Expression> results) {
261+
private @Nullable String getWhenTemplate(List<Expression> results) {
246262
boolean buildingResults = false;
247-
final StringBuilder templateBuilder = new StringBuilder(WHEN_TEMPLATE_PREFIX);
263+
StringBuilder templateBuilder = new StringBuilder();
264+
if (this.blockType == NonStrictExpectations) {
265+
templateBuilder.append(LENIENT_TEMPLATE_PREFIX);
266+
}
267+
templateBuilder.append(WHEN_TEMPLATE_PREFIX);
248268
for (Expression result : results) {
249269
JavaType resultType = result.getType();
250270
if (result instanceof J.Literal) {
@@ -284,10 +304,9 @@ private static void appendToTemplate(StringBuilder templateBuilder, boolean buil
284304
templateBuilder.append(templateField);
285305
}
286306

287-
private static String getVerifyTemplate(List<Expression> arguments, @Nullable String
288-
verificationMode, List<Object> templateParams) {
289-
StringBuilder templateBuilder = new StringBuilder("verify(#{any()}"); // eg verify(object
290-
if (verificationMode != null) {
307+
private static String getVerifyTemplate(List<Expression> arguments, String verificationMode, List<Object> templateParams) {
308+
StringBuilder templateBuilder = new StringBuilder(VERIFY_TEMPLATE_PREFIX); // eg verify(object
309+
if (!verificationMode.isEmpty()) {
291310
templateBuilder.append(", ").append(verificationMode).append("(#{any(int)})"); // eg verify(object, times(2)
292311
}
293312
templateBuilder.append(").#{}("); // eg verify(object, times(2)).method(

src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockToMockito.java

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,26 +29,28 @@
2929
import java.util.List;
3030
import java.util.Optional;
3131

32+
import static org.openrewrite.java.testing.jmockit.JMockitBlockType.*;
33+
3234
@Value
3335
@EqualsAndHashCode(callSuper = false)
3436
public class JMockitBlockToMockito extends Recipe {
3537

3638
@Override
3739
public String getDisplayName() {
38-
return "Rewrite JMockit Expectations and Verifications";
40+
return "Rewrite JMockit Expectations, Verifications and NonStrictExpectations";
3941
}
4042

4143
@Override
4244
public String getDescription() {
43-
return "Rewrites JMockit `Expectations and Verifications` blocks to Mockito statements.";
45+
return "Rewrites JMockit `Expectations, Verifications and NonStrictExpectations` blocks to Mockito statements.";
4446
}
4547

4648
@Override
4749
public TreeVisitor<?, ExecutionContext> getVisitor() {
4850
return Preconditions.check(Preconditions.or(
49-
new UsesType<>(JMockitBlockType.Expectations.getFqn(), false),
50-
new UsesType<>(JMockitBlockType.Verifications.getFqn(), false)
51-
), new RewriteJMockitBlockVisitor());
51+
new UsesType<>(Expectations.getFqn(), false),
52+
new UsesType<>(Verifications.getFqn(), false),
53+
new UsesType<>(NonStrictExpectations.getFqn(), false)), new RewriteJMockitBlockVisitor());
5254
}
5355

5456
private static class RewriteJMockitBlockVisitor extends JavaIsoVisitor<ExecutionContext> {

src/main/java/org/openrewrite/java/testing/jmockit/JMockitBlockType.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,17 @@
1616
package org.openrewrite.java.testing.jmockit;
1717

1818
import lombok.Getter;
19-
import lombok.RequiredArgsConstructor;
2019

21-
@RequiredArgsConstructor
2220
@Getter
2321
enum JMockitBlockType {
2422

25-
Expectations("mockit.Expectations"),
26-
Verifications("mockit.Verifications"); // Add NonStrictExpectations later
23+
Expectations,
24+
Verifications,
25+
NonStrictExpectations;
2726

2827
private final String fqn;
28+
29+
JMockitBlockType() {
30+
this.fqn = "mockit." + this.name();
31+
}
2932
}

src/main/java/org/openrewrite/java/testing/jmockit/JMockitUtils.java

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,9 @@ static Optional<JMockitBlockType> getJMockitBlock(Statement s) {
4040
return empty();
4141
}
4242

43-
for (JMockitBlockType blockType : JMockitBlockType.values()) {
44-
if (TypeUtils.isAssignableTo(blockType.getFqn(), clazz.getType())) {
45-
return Optional.of(blockType);
46-
}
43+
JMockitBlockType blockType = JMockitBlockType.valueOf(clazz.getSimpleName());
44+
if (blockType != null && TypeUtils.isOfClassType(clazz.getType(), blockType.getFqn())) {
45+
return Optional.of(blockType);
4746
}
4847
return empty();
4948
}
601 KB
Binary file not shown.

src/main/resources/META-INF/rewrite/jmockit.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,9 @@ recipeList:
3636
- org.openrewrite.java.ChangeType:
3737
oldFullyQualifiedTypeName: mockit.integration.junit5.JMockitExtension
3838
newFullyQualifiedTypeName: org.mockito.junit.jupiter.MockitoExtension
39+
- org.openrewrite.java.ChangeType:
40+
oldFullyQualifiedTypeName: mockit.integration.junit4.JMockit
41+
newFullyQualifiedTypeName: org.mockito.junit.MockitoJUnitRunner
3942
- org.openrewrite.java.dependencies.AddDependency:
4043
groupId: org.mockito
4144
artifactId: mockito-core

src/test/java/org/openrewrite/java/testing/jmockit/JMockitExpectationsToMockitoTest.java

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,29 +17,17 @@
1717

1818
import org.junit.jupiter.api.Test;
1919
import org.openrewrite.DocumentExample;
20-
import org.openrewrite.InMemoryExecutionContext;
21-
import org.openrewrite.java.JavaParser;
2220
import org.openrewrite.test.RecipeSpec;
2321
import org.openrewrite.test.RewriteTest;
2422

2523
import static org.openrewrite.java.Assertions.java;
24+
import static org.openrewrite.java.testing.jmockit.JMockitTestUtils.setDefaultParserSettings;
2625

2726
class JMockitExpectationsToMockitoTest implements RewriteTest {
27+
2828
@Override
2929
public void defaults(RecipeSpec spec) {
30-
spec
31-
.parser(JavaParser.fromJavaVersion()
32-
.logCompilationWarningsAndErrors(true)
33-
.classpathFromResources(new InMemoryExecutionContext(),
34-
"junit-jupiter-api-5.9",
35-
"jmockit-1.49",
36-
"mockito-core-3.12",
37-
"mockito-junit-jupiter-3.12"
38-
))
39-
.recipeFromResource(
40-
"/META-INF/rewrite/jmockit.yml",
41-
"org.openrewrite.java.testing.jmockit.JMockitToMockito"
42-
);
30+
setDefaultParserSettings(spec);
4331
}
4432

4533
@DocumentExample

0 commit comments

Comments
 (0)