2525import org .openrewrite .java .JavaIsoVisitor ;
2626import org .openrewrite .java .MethodMatcher ;
2727import org .openrewrite .java .search .UsesMethod ;
28+ import org .openrewrite .java .tree .Expression ;
2829import org .openrewrite .java .tree .J ;
2930import org .openrewrite .java .tree .Statement ;
3031
3132import java .util .HashSet ;
3233import java .util .Set ;
34+ import java .util .concurrent .atomic .AtomicBoolean ;
3335
3436public class RemoveDoNothingForDefaultMocks extends Recipe {
3537
@@ -43,6 +45,7 @@ public class RemoveDoNothingForDefaultMocks extends Recipe {
4345
4446 private static final MethodMatcher DO_NOTHING_MATCHER = new MethodMatcher ("org.mockito.Mockito doNothing()" );
4547 private static final MethodMatcher STUBBER_WHEN_MATCHER = new MethodMatcher ("org.mockito.stubbing.Stubber when(..)" );
48+ private static final MethodMatcher CAPTURE_MATCHER = new MethodMatcher ("org.mockito.ArgumentCaptor capture()" );
4649 private static final AnnotationMatcher MOCK_ANNOTATION_MATCHER = new AnnotationMatcher ("@org.mockito.Mock" );
4750
4851 @ Override
@@ -100,7 +103,33 @@ private boolean isDoNothingOnMockField(J.MethodInvocation mi) {
100103 }
101104 String mockName = ((J .Identifier ) whenCall .getArguments ().get (0 )).getSimpleName ();
102105 Set <String > mockFieldNames = getCursor ().getNearestMessage ("mockFieldNames" );
103- return mockFieldNames != null && mockFieldNames .contains (mockName );
106+ if (mockFieldNames == null || !mockFieldNames .contains (mockName )) {
107+ return false ;
108+ }
109+ // Preserve stubbings whose arguments include ArgumentCaptor.capture(),
110+ // which registers a matcher used to capture later real invocations.
111+ return !containsCapture (mi .getArguments ());
112+ }
113+
114+ private boolean containsCapture (java .util .List <Expression > arguments ) {
115+ AtomicBoolean found = new AtomicBoolean ();
116+ JavaIsoVisitor <AtomicBoolean > visitor = new JavaIsoVisitor <AtomicBoolean >() {
117+ @ Override
118+ public J .MethodInvocation visitMethodInvocation (J .MethodInvocation method , AtomicBoolean acc ) {
119+ if (CAPTURE_MATCHER .matches (method )) {
120+ acc .set (true );
121+ return method ;
122+ }
123+ return super .visitMethodInvocation (method , acc );
124+ }
125+ };
126+ for (Expression arg : arguments ) {
127+ visitor .visit (arg , found );
128+ if (found .get ()) {
129+ return true ;
130+ }
131+ }
132+ return false ;
104133 }
105134 }
106135 );
0 commit comments