2121import org .jspecify .annotations .Nullable ;
2222import org .openrewrite .Cursor ;
2323import org .openrewrite .ExecutionContext ;
24+ import org .openrewrite .Tree ;
2425import org .openrewrite .java .JavaTemplate ;
2526import org .openrewrite .java .JavaVisitor ;
2627import org .openrewrite .java .tree .*;
28+ import org .openrewrite .marker .Markers ;
2729
2830import java .util .ArrayList ;
2931import java .util .List ;
3032
33+ import static java .util .stream .Collectors .toList ;
3134import static org .openrewrite .java .testing .jmockit .JMockitBlockType .*;
3235import static org .openrewrite .java .testing .jmockit .JMockitUtils .MOCKITO_ALL_IMPORT ;
3336import static org .openrewrite .java .testing .jmockit .JMockitUtils .getJavaParser ;
@@ -70,6 +73,10 @@ boolean isRewriteFailed() {
7073 // used with bodyStatementIndex to obtain the coordinates of the next statement to be written
7174 private int numStatementsAdded = 0 ;
7275
76+ // Track setup statements that need to be preserved and wrapped in a block
77+ private final List <Statement > setupStatementsBeforeFirstMock = new ArrayList <>();
78+ private boolean hasSetupStatements = false ;
79+
7380 JMockitBlockRewriter (JavaVisitor <ExecutionContext > visitor , ExecutionContext ctx , J .Block methodBody ,
7481 J .NewClass newExpectations , int bodyStatementIndex , JMockitBlockType blockType , int verificationsInOrderIdx ) {
7582 this .visitor = visitor ;
@@ -102,6 +109,17 @@ J.Block rewriteMethodBody() {
102109 List <J .Identifier > uniqueMocks = new ArrayList <>();
103110 int methodInvocationIdx = -1 ;
104111 for (Statement jmockitBlockStatement : jmockitBlock .getStatements ()) {
112+ // Track setup statements (variable declarations) that appear before mock invocations
113+ if (isSetupStatement (jmockitBlockStatement )) {
114+ hasSetupStatements = true ;
115+ if (methodInvocationIdx == -1 ) {
116+ // Setup statement before the first mock invocation
117+ setupStatementsBeforeFirstMock .add (jmockitBlockStatement );
118+ continue ;
119+ }
120+ // Setup statement after a mock invocation - will be handled by the group
121+ }
122+
105123 if (jmockitBlockStatement instanceof J .MethodInvocation ) {
106124 J .MethodInvocation invocation = (J .MethodInvocation ) jmockitBlockStatement ;
107125 Expression select = invocation .getSelect ();
@@ -130,6 +148,9 @@ J.Block rewriteMethodBody() {
130148 removeBlock ();
131149 }
132150
151+ // Output setup statements that come before the first mock invocation
152+ outputSetupStatements (setupStatementsBeforeFirstMock );
153+
133154 List <Object > mocks = new ArrayList <>(uniqueMocks );
134155 if (isVerificationsInOrder ()) {
135156 rewriteInOrderVerify (mocks );
@@ -141,9 +162,61 @@ J.Block rewriteMethodBody() {
141162 if (isFullVerifications ()) {
142163 rewriteFullVerify (mocks );
143164 }
165+
166+ // If there were setup statements, wrap the generated statements in a block
167+ // to avoid variable name conflicts with the rest of the method
168+ if (hasSetupStatements ) {
169+ wrapStatementsInBlock ();
170+ }
171+
144172 return methodBody ;
145173 }
146174
175+ private void wrapStatementsInBlock () {
176+ List <Statement > statements = methodBody .getStatements ();
177+ if (numStatementsAdded <= 0 ) {
178+ return ;
179+ }
180+
181+ // Get the statements that were added (from bodyStatementIndex to bodyStatementIndex + numStatementsAdded - 1)
182+ int startIdx = bodyStatementIndex ;
183+ int endIdx = bodyStatementIndex + numStatementsAdded ;
184+
185+ if (startIdx < 0 || endIdx > statements .size ()) {
186+ return ;
187+ }
188+
189+ // Extract the statements to wrap
190+ List <Statement > statementsToWrap = new ArrayList <>();
191+ for (int i = startIdx ; i < endIdx ; i ++) {
192+ statementsToWrap .add (statements .get (i ));
193+ }
194+
195+ // Create a new block containing these statements
196+ J .Block wrapperBlock = new J .Block (
197+ Tree .randomId (),
198+ Space .EMPTY ,
199+ Markers .EMPTY ,
200+ JRightPadded .build (false ),
201+ statementsToWrap .stream ()
202+ .map (JRightPadded ::build )
203+ .collect (toList ()),
204+ Space .EMPTY
205+ );
206+
207+ // Replace the statements with the block
208+ List <Statement > newStatements = new ArrayList <>();
209+ for (int i = 0 ; i < startIdx ; i ++) {
210+ newStatements .add (statements .get (i ));
211+ }
212+ newStatements .add (wrapperBlock );
213+ for (int i = endIdx ; i < statements .size (); i ++) {
214+ newStatements .add (statements .get (i ));
215+ }
216+
217+ methodBody = methodBody .withStatements (newStatements );
218+ }
219+
147220 private boolean isFullVerifications () {
148221 return this .blockType == FullVerifications ;
149222 }
@@ -152,6 +225,11 @@ private boolean isVerificationsInOrder() {
152225 return this .blockType == VerificationsInOrder ;
153226 }
154227
228+ private static boolean isSetupStatement (Statement statement ) {
229+ // Variable declarations are setup statements
230+ return statement instanceof J .VariableDeclarations ;
231+ }
232+
155233 private void rewriteMethodInvocation (List <Statement > statementsToRewrite ) {
156234 final MockInvocationResults mockInvocationResults = buildMockInvocationResults (statementsToRewrite );
157235 if (mockInvocationResults == null ) {
@@ -169,6 +247,8 @@ private void rewriteMethodInvocation(List<Statement> statementsToRewrite) {
169247
170248 if (!hasResults && !hasTimes && (this .blockType == JMockitBlockType .Expectations || this .blockType .isVerifications ())) {
171249 rewriteVerify (invocation , null , "" );
250+ // Output setup statements that follow this mock invocation
251+ outputSetupStatements (mockInvocationResults .getSetupStatements ());
172252 return ;
173253 }
174254 if (mockInvocationResults .getTimes () != null ) {
@@ -185,6 +265,15 @@ private void rewriteMethodInvocation(List<Statement> statementsToRewrite) {
185265 if (mockInvocationResults .getMaxTimes () != null ) {
186266 rewriteVerify (invocation , mockInvocationResults .getMaxTimes (), "atMost" );
187267 }
268+
269+ // Output setup statements that follow this mock invocation
270+ outputSetupStatements (mockInvocationResults .getSetupStatements ());
271+ }
272+
273+ private void outputSetupStatements (List <Statement > setupStatements ) {
274+ for (Statement setupStatement : setupStatements ) {
275+ insertStatementAtCurrentPosition (setupStatement );
276+ }
188277 }
189278
190279 private void removeBlock () {
@@ -196,6 +285,20 @@ private void removeBlock() {
196285 setNextStatementCoordinates (0 );
197286 }
198287
288+ private void insertStatementAtCurrentPosition (Statement statement ) {
289+ List <Statement > statements = new ArrayList <>(methodBody .getStatements ());
290+ int insertIdx = bodyStatementIndex + numStatementsAdded ;
291+ if (insertIdx < 0 ) {
292+ insertIdx = 0 ;
293+ }
294+ if (insertIdx > statements .size ()) {
295+ insertIdx = statements .size ();
296+ }
297+ statements .add (insertIdx , statement );
298+ methodBody = methodBody .withStatements (statements );
299+ setNextStatementCoordinates (++numStatementsAdded );
300+ }
301+
199302 private void rewriteResult (J .MethodInvocation invocation , List <Expression > results , boolean hasTimes ) {
200303 boolean lenient = this .blockType == NonStrictExpectations && !hasTimes ;
201304 String template = getWhenTemplate (results , lenient );
@@ -457,6 +560,15 @@ private static void appendToTemplate(StringBuilder templateBuilder, boolean buil
457560 }
458561 continue ;
459562 }
563+ // Skip setup statements (variable declarations) - they're tracked separately
564+ if (isSetupStatement (expectationStatement )) {
565+ resultWrapper .addSetupStatement (expectationStatement );
566+ continue ;
567+ }
568+ if (!(expectationStatement instanceof J .Assignment )) {
569+ // unhandled statement type, skip it
570+ continue ;
571+ }
460572 J .Assignment assignment = (J .Assignment ) expectationStatement ;
461573 String variableName = getVariableNameFromAssignment (assignment );
462574 if (variableName == null ) {
@@ -525,6 +637,8 @@ private static void appendToTemplate(StringBuilder templateBuilder, boolean buil
525637 private static class MockInvocationResults {
526638 @ Setter (AccessLevel .NONE )
527639 private final List <Expression > results = new ArrayList <>();
640+ @ Setter (AccessLevel .NONE )
641+ private final List <Statement > setupStatements = new ArrayList <>();
528642 private @ Nullable Expression times ;
529643 private @ Nullable Expression minTimes ;
530644 private @ Nullable Expression maxTimes ;
@@ -533,6 +647,10 @@ private void addResult(Expression result) {
533647 results .add (result );
534648 }
535649
650+ private void addSetupStatement (Statement statement ) {
651+ setupStatements .add (statement );
652+ }
653+
536654 private boolean hasAnyTimes () {
537655 return times != null || minTimes != null || maxTimes != null ;
538656 }
0 commit comments