Skip to content

Commit 5a2d834

Browse files
JMockit NonStrictExpectations with times should be migrated to default when without lenient(), refactor (#549)
* 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 * When putting times in NonStrictExpectations ensure it is strict stubbing and add test cases, also refactor * Refactor logic for imports to make it cleaner * Qualify `rewriteFailed` consistently --------- Co-authored-by: Tim te Beek <[email protected]>
1 parent a79dc45 commit 5a2d834

File tree

3 files changed

+257
-50
lines changed

3 files changed

+257
-50
lines changed

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

Lines changed: 50 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ boolean isRewriteFailed() {
7575
this.newExpectations = newExpectations;
7676
this.bodyStatementIndex = bodyStatementIndex;
7777
this.blockType = blockType;
78-
nextStatementCoordinates = newExpectations.getCoordinates().replace();
78+
this.nextStatementCoordinates = newExpectations.getCoordinates().replace();
7979
}
8080

8181
J.Block rewriteMethodBody() {
@@ -125,74 +125,63 @@ private void rewriteMethodInvocation(List<Statement> statementsToRewrite) {
125125
final MockInvocationResults mockInvocationResults = buildMockInvocationResults(statementsToRewrite);
126126
if (mockInvocationResults == null) {
127127
// invalid block, cannot rewrite
128-
rewriteFailed = true;
128+
this.rewriteFailed = true;
129129
return;
130130
}
131131

132132
J.MethodInvocation invocation = (J.MethodInvocation) statementsToRewrite.get(0);
133133
boolean hasResults = !mockInvocationResults.getResults().isEmpty();
134+
boolean hasTimes = mockInvocationResults.hasAnyTimes();
134135
if (hasResults) {
135-
rewriteResult(invocation, mockInvocationResults.getResults());
136+
rewriteResult(invocation, mockInvocationResults.getResults(), hasTimes);
136137
}
137138

138-
if (blockType == NonStrictExpectations) {
139-
// no verify for NonStrictExpectations
139+
if (!hasResults && !hasTimes && (this.blockType == JMockitBlockType.Expectations || this.blockType == Verifications)) {
140+
rewriteVerify(invocation, null, "");
140141
return;
141142
}
142-
143-
boolean hasTimes = false;
144143
if (mockInvocationResults.getTimes() != null) {
145-
hasTimes = true;
146144
rewriteVerify(invocation, mockInvocationResults.getTimes(), "times");
147145
}
148146
if (mockInvocationResults.getMinTimes() != null) {
149-
hasTimes = true;
150147
rewriteVerify(invocation, mockInvocationResults.getMinTimes(), "atLeast");
151148
}
152149
if (mockInvocationResults.getMaxTimes() != null) {
153-
hasTimes = true;
154150
rewriteVerify(invocation, mockInvocationResults.getMaxTimes(), "atMost");
155151
}
156-
if (!hasResults && !hasTimes) {
157-
rewriteVerify(invocation, null, "");
158-
}
159152
}
160153

161154
private void removeBlock() {
162155
methodBody = JavaTemplate.builder("")
163156
.javaParser(JavaParser.fromJavaVersion())
164157
.build()
165158
.apply(new Cursor(visitor.getCursor(), methodBody), nextStatementCoordinates);
166-
if (bodyStatementIndex == 0) {
167-
nextStatementCoordinates = methodBody.getCoordinates().firstStatement();
168-
} else {
169-
setNextStatementCoordinates(0);
170-
}
159+
setNextStatementCoordinates(0);
171160
}
172161

173-
private void rewriteResult(J.MethodInvocation invocation, List<Expression> results) {
174-
String template = getWhenTemplate(results);
162+
private void rewriteResult(J.MethodInvocation invocation, List<Expression> results, boolean hasTimes) {
163+
boolean lenient = this.blockType == NonStrictExpectations && !hasTimes;
164+
String template = getWhenTemplate(results, lenient);
175165
if (template == null) {
176166
// invalid template, cannot rewrite
177-
rewriteFailed = true;
167+
this.rewriteFailed = true;
178168
return;
179169
}
180170

181171
List<Object> templateParams = new ArrayList<>();
182172
templateParams.add(invocation);
183173
templateParams.addAll(results);
184174
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);
175+
if (this.rewriteFailed) {
176+
return;
177+
}
178+
179+
setNextStatementCoordinates(++numStatementsAdded);
180+
// do this last making sure rewrite worked and specify onlyifReferenced=false because framework cannot find static
181+
// reference for when method invocation when another static mockit reference is added
182+
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, "when", false);
183+
if (lenient) {
184+
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, "lenient");
196185
}
197186
}
198187

@@ -218,29 +207,37 @@ private void rewriteVerify(J.MethodInvocation invocation, @Nullable Expression t
218207
verifyCoordinates = methodBody.getCoordinates().lastStatement();
219208
}
220209
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-
}
210+
if (this.rewriteFailed) {
211+
return;
212+
}
225213

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-
}
214+
if (this.blockType == Verifications) {
215+
setNextStatementCoordinates(++numStatementsAdded); // for Expectations, verify statements added to end of method
216+
}
217+
218+
// do this last making sure rewrite worked and specify onlyifReferenced=false because framework cannot find the
219+
// static reference to verify when another static mockit reference is added
220+
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, "verify", false);
221+
if (!verificationMode.isEmpty()) {
222+
visitor.maybeAddImport(MOCKITO_IMPORT_FQN_PREFX, verificationMode);
232223
}
233224
}
234225

235226
private void setNextStatementCoordinates(int numStatementsAdded) {
227+
if (numStatementsAdded <= 0 && bodyStatementIndex == 0) {
228+
nextStatementCoordinates = methodBody.getCoordinates().firstStatement();
229+
return;
230+
}
231+
236232
// the next statement coordinates are directly after the most recently written statement, calculated by
237233
// subtracting the removed jmockit block
238-
int nextStatementIdx = bodyStatementIndex + numStatementsAdded - 1;
239-
if (nextStatementIdx >= this.methodBody.getStatements().size()) {
240-
rewriteFailed = true;
241-
} else {
242-
this.nextStatementCoordinates = this.methodBody.getStatements().get(nextStatementIdx).getCoordinates().after();
234+
int lastStatementIdx = bodyStatementIndex + numStatementsAdded - 1;
235+
if (lastStatementIdx >= this.methodBody.getStatements().size()) {
236+
this.rewriteFailed = true;
237+
return;
243238
}
239+
240+
this.nextStatementCoordinates = this.methodBody.getStatements().get(lastStatementIdx).getCoordinates().after();
244241
}
245242

246243
private boolean rewriteTemplate(String template, List<Object> templateParams, JavaCoordinates
@@ -258,10 +255,10 @@ private boolean rewriteTemplate(String template, List<Object> templateParams, Ja
258255
return methodBody.getStatements().size() > numStatementsBefore;
259256
}
260257

261-
private @Nullable String getWhenTemplate(List<Expression> results) {
258+
private @Nullable String getWhenTemplate(List<Expression> results, boolean lenient) {
262259
boolean buildingResults = false;
263260
StringBuilder templateBuilder = new StringBuilder();
264-
if (this.blockType == NonStrictExpectations) {
261+
if (lenient) {
265262
templateBuilder.append(LENIENT_TEMPLATE_PREFIX);
266263
}
267264
templateBuilder.append(WHEN_TEMPLATE_PREFIX);
@@ -422,5 +419,9 @@ private static class MockInvocationResults {
422419
private void addResult(Expression result) {
423420
results.add(result);
424421
}
422+
423+
private boolean hasAnyTimes() {
424+
return times != null || minTimes != null || maxTimes != null;
425+
}
425426
}
426427
}

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

Lines changed: 105 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,61 @@ public void defaults(RecipeSpec spec) {
3030
setDefaultParserSettings(spec);
3131
}
3232

33+
@DocumentExample
34+
@Test
35+
void whenTimesAndResult() {
36+
//language=java
37+
rewriteRun(
38+
java(
39+
"""
40+
import mockit.Expectations;
41+
import mockit.Mocked;
42+
import mockit.integration.junit5.JMockitExtension;
43+
import org.junit.jupiter.api.extension.ExtendWith;
44+
45+
import static org.junit.jupiter.api.Assertions.assertEquals;
46+
47+
@ExtendWith(JMockitExtension.class)
48+
class MyTest {
49+
@Mocked
50+
Object myObject;
51+
52+
void test() {
53+
new Expectations() {{
54+
myObject.toString();
55+
result = "foo";
56+
times = 2;
57+
}};
58+
assertEquals("foo", myObject.toString());
59+
assertEquals("foo", myObject.toString());
60+
}
61+
}
62+
""",
63+
"""
64+
import org.junit.jupiter.api.extension.ExtendWith;
65+
import org.mockito.Mock;
66+
import org.mockito.junit.jupiter.MockitoExtension;
67+
68+
import static org.junit.jupiter.api.Assertions.assertEquals;
69+
import static org.mockito.Mockito.*;
70+
71+
@ExtendWith(MockitoExtension.class)
72+
class MyTest {
73+
@Mock
74+
Object myObject;
75+
76+
void test() {
77+
when(myObject.toString()).thenReturn("foo");
78+
assertEquals("foo", myObject.toString());
79+
assertEquals("foo", myObject.toString());
80+
verify(myObject, times(2)).toString();
81+
}
82+
}
83+
"""
84+
)
85+
);
86+
}
87+
3388
@DocumentExample
3489
@Test
3590
void whenNoResultNoTimes() {
@@ -77,7 +132,6 @@ void test() {
77132
);
78133
}
79134

80-
@DocumentExample
81135
@Test
82136
void whenNoResultNoTimesNoArgs() {
83137
//language=java
@@ -124,6 +178,56 @@ void test() {
124178
);
125179
}
126180

181+
@Test
182+
void whenHasResultNoTimes() {
183+
//language=java
184+
rewriteRun(
185+
java(
186+
"""
187+
import mockit.Expectations;
188+
import mockit.Mocked;
189+
import mockit.integration.junit5.JMockitExtension;
190+
import org.junit.jupiter.api.extension.ExtendWith;
191+
192+
import static org.junit.jupiter.api.Assertions.assertEquals;
193+
194+
@ExtendWith(JMockitExtension.class)
195+
class MyTest {
196+
@Mocked
197+
Object myObject;
198+
199+
void test() {
200+
new Expectations() {{
201+
myObject.toString();
202+
result = "foo";
203+
}};
204+
assertEquals("foo", myObject.toString());
205+
}
206+
}
207+
""",
208+
"""
209+
import org.junit.jupiter.api.extension.ExtendWith;
210+
import org.mockito.Mock;
211+
import org.mockito.junit.jupiter.MockitoExtension;
212+
213+
import static org.junit.jupiter.api.Assertions.assertEquals;
214+
import static org.mockito.Mockito.when;
215+
216+
@ExtendWith(MockitoExtension.class)
217+
class MyTest {
218+
@Mock
219+
Object myObject;
220+
221+
void test() {
222+
when(myObject.toString()).thenReturn("foo");
223+
assertEquals("foo", myObject.toString());
224+
}
225+
}
226+
"""
227+
)
228+
);
229+
}
230+
127231
@Test
128232
void whenNullResult() {
129233
//language=java

0 commit comments

Comments
 (0)