Skip to content

Commit b06f6a4

Browse files
authored
Preserve doNothing() stubbings using ArgumentCaptor.capture() (#988)
The captor registers a matcher used to capture values during later real invocations; removing the stubbing breaks the capture.
1 parent 41ace45 commit b06f6a4

2 files changed

Lines changed: 89 additions & 1 deletion

File tree

src/main/java/org/openrewrite/java/testing/mockito/RemoveDoNothingForDefaultMocks.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@
2525
import org.openrewrite.java.JavaIsoVisitor;
2626
import org.openrewrite.java.MethodMatcher;
2727
import org.openrewrite.java.search.UsesMethod;
28+
import org.openrewrite.java.tree.Expression;
2829
import org.openrewrite.java.tree.J;
2930
import org.openrewrite.java.tree.Statement;
3031

3132
import java.util.HashSet;
3233
import java.util.Set;
34+
import java.util.concurrent.atomic.AtomicBoolean;
3335

3436
public 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
);

src/test/java/org/openrewrite/java/testing/mockito/RemoveDoNothingForDefaultMocksTest.java

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,65 @@ public void test() throws IOException {
279279
);
280280
}
281281

282+
@Test
283+
void retainsDoNothingWithArgumentCaptor() {
284+
rewriteRun(
285+
//language=Java
286+
java(
287+
"""
288+
import org.mockito.ArgumentCaptor;
289+
import org.mockito.Mock;
290+
291+
import static org.mockito.Mockito.doNothing;
292+
293+
class ExampleTest {
294+
@Mock
295+
Client client;
296+
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
297+
298+
void setUp() {
299+
doNothing().when(client).send(captor.capture());
300+
}
301+
302+
interface Client {
303+
void send(String message);
304+
}
305+
}
306+
"""
307+
)
308+
);
309+
}
310+
311+
@Test
312+
void retainsDoNothingWithNestedArgumentCaptor() {
313+
rewriteRun(
314+
//language=Java
315+
java(
316+
"""
317+
import org.mockito.ArgumentCaptor;
318+
import org.mockito.Mock;
319+
320+
import static org.mockito.ArgumentMatchers.eq;
321+
import static org.mockito.Mockito.doNothing;
322+
323+
class ExampleTest {
324+
@Mock
325+
Client client;
326+
ArgumentCaptor<String> captor = ArgumentCaptor.forClass(String.class);
327+
328+
void setUp() {
329+
doNothing().when(client).send(eq("prefix"), captor.capture());
330+
}
331+
332+
interface Client {
333+
void send(String prefix, String message);
334+
}
335+
}
336+
"""
337+
)
338+
);
339+
}
340+
282341
@Test
283342
void noChangeWithoutDoNothing() {
284343
rewriteRun(

0 commit comments

Comments
 (0)