Skip to content

Commit 2874cd2

Browse files
garyrussellartembilan
authored andcommitted
Mockito Answers: Capture any exceptions
1 parent e37e9aa commit 2874cd2

File tree

6 files changed

+80
-23
lines changed

6 files changed

+80
-23
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ erl_crash.dump
1919
nohup.out
2020
src/ant/.ant-targets-upload-dist.xml
2121
target
22+
.sts4-cache

spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/mockito/LambdaAnswer.java

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,17 @@
1616

1717
package org.springframework.amqp.rabbit.test.mockito;
1818

19+
import java.util.Collection;
20+
import java.util.Collections;
21+
import java.util.LinkedHashSet;
22+
import java.util.Set;
23+
1924
import org.mockito.invocation.InvocationOnMock;
2025
import org.mockito.stubbing.Answer;
2126

2227
/**
23-
* An Answer to optionally call the real method and allow returning a
24-
* custom result.
28+
* An {@link Answer} to optionally call the real method and allow returning a
29+
* custom result. Captures any exceptions thrown.
2530
*
2631
* @author Gary Russell
2732
* @since 1.6
@@ -33,6 +38,8 @@ public class LambdaAnswer<T> implements Answer<T> {
3338

3439
private final ValueToReturn<T> callback;
3540

41+
private final Set<Exception> exceptions = Collections.synchronizedSet(new LinkedHashSet<>());
42+
3643
public LambdaAnswer(boolean callRealMethod, ValueToReturn<T> callback) {
3744
this.callRealMethod = callRealMethod;
3845
this.callback = callback;
@@ -42,12 +49,28 @@ public LambdaAnswer(boolean callRealMethod, ValueToReturn<T> callback) {
4249
@Override
4350
public T answer(InvocationOnMock invocation) throws Throwable {
4451
T result = null;
45-
if (this.callRealMethod) {
46-
result = (T) invocation.callRealMethod();
52+
try {
53+
if (this.callRealMethod) {
54+
result = (T) invocation.callRealMethod();
55+
}
56+
return this.callback.apply(invocation, result);
57+
}
58+
catch (Exception e) {
59+
this.exceptions.add(e);
60+
throw e;
4761
}
48-
return this.callback.apply(invocation, result);
4962
}
5063

64+
/**
65+
* Return the exceptions thrown, if any.
66+
* @return the exceptions.
67+
* @since 2.2.3
68+
*/
69+
public Collection<Exception> getExceptions() {
70+
return Collections.unmodifiableCollection(this.exceptions);
71+
}
72+
73+
@FunctionalInterface
5174
public interface ValueToReturn<T> {
5275

5376
T apply(InvocationOnMock invocation, T result);

spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/mockito/LatchCountDownAndCallRealMethodAnswer.java

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,20 @@
1616

1717
package org.springframework.amqp.rabbit.test.mockito;
1818

19+
import java.util.Collection;
20+
import java.util.Collections;
21+
import java.util.LinkedHashSet;
22+
import java.util.Set;
1923
import java.util.concurrent.CountDownLatch;
2024

2125
import org.mockito.invocation.InvocationOnMock;
2226
import org.mockito.stubbing.Answer;
2327

28+
import org.springframework.lang.Nullable;
29+
2430
/**
25-
* An Answer for void returning methods that calls the real method and
26-
* counts down a latch.
31+
* An {@link Answer} for void returning methods that calls the real method and counts down
32+
* a latch. Captures any exceptions thrown.
2733
*
2834
* @author Gary Russell
2935
* @since 1.6
@@ -33,6 +39,8 @@ public class LatchCountDownAndCallRealMethodAnswer implements Answer<Void> {
3339

3440
private final CountDownLatch latch;
3541

42+
private final Set<Exception> exceptions = Collections.synchronizedSet(new LinkedHashSet<>());
43+
3644
/**
3745
* @param count to set in a {@link CountDownLatch}.
3846
*/
@@ -42,8 +50,16 @@ public LatchCountDownAndCallRealMethodAnswer(int count) {
4250

4351
@Override
4452
public Void answer(InvocationOnMock invocation) throws Throwable {
45-
invocation.callRealMethod();
46-
this.latch.countDown();
53+
try {
54+
invocation.callRealMethod();
55+
}
56+
catch (Exception e) {
57+
this.exceptions.add(e);
58+
throw e;
59+
}
60+
finally {
61+
this.latch.countDown();
62+
}
4763
return null;
4864
}
4965

@@ -52,4 +68,14 @@ public CountDownLatch getLatch() {
5268
return latch;
5369
}
5470

71+
/**
72+
* Return the exceptions thrown.
73+
* @return the exceptions.
74+
* @since 2.2.3
75+
*/
76+
@Nullable
77+
public Collection<Exception> getExceptions() {
78+
return Collections.unmodifiableCollection(this.exceptions);
79+
}
80+
5581
}

spring-rabbit-test/src/test/java/org/springframework/amqp/rabbit/test/ExampleRabbitListenerSpyAndCaptureTest.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import static org.mockito.Mockito.doAnswer;
2222
import static org.mockito.Mockito.verify;
2323

24+
import java.util.Collection;
2425
import java.util.concurrent.TimeUnit;
2526

2627
import org.junit.jupiter.api.Test;
@@ -122,6 +123,10 @@ public void testOneWay() throws Exception {
122123
assertThat((String) args[0]).isEqualTo("ex");
123124
assertThat((String) args[1]).isEqualTo(queue2.getName());
124125
assertThat(invocationData.getThrowable()).isNull();
126+
127+
Collection<Exception> exceptions = answer.getExceptions();
128+
assertThat(exceptions).hasSize(1);
129+
assertThat(exceptions.iterator().next()).isInstanceOf(IllegalArgumentException.class);
125130
}
126131

127132
@Configuration
@@ -177,10 +182,10 @@ public String foo(String foo) {
177182
}
178183

179184
@RabbitListener(id = "bar", queues = "#{queue2.name}")
180-
public void foo(@Payload String foo, @Header("amqp_receivedRoutingKey") String rk) {
185+
public void foo(@Payload String foo, @SuppressWarnings("unused") @Header("amqp_receivedRoutingKey") String rk) {
181186
if (!failed && foo.equals("ex")) {
182187
failed = true;
183-
throw new RuntimeException(foo);
188+
throw new IllegalArgumentException(foo);
184189
}
185190
failed = false;
186191
}

spring-rabbit-test/src/test/java/org/springframework/amqp/rabbit/test/mockito/AnswerTests.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,13 @@
1717
package org.springframework.amqp.rabbit.test.mockito;
1818

1919
import static org.assertj.core.api.Assertions.assertThat;
20+
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
2021
import static org.mockito.ArgumentMatchers.anyString;
2122
import static org.mockito.BDDMockito.willAnswer;
2223
import static org.mockito.Mockito.spy;
2324

25+
import java.util.Collection;
26+
2427
import org.junit.jupiter.api.Test;
2528

2629
/**
@@ -40,6 +43,12 @@ public void testLambda() {
4043
willAnswer(new LambdaAnswer<String>(false, (i, r) ->
4144
"" + i.getArguments()[0] + i.getArguments()[0])).given(foo).foo(anyString());
4245
assertThat(foo.foo("foo")).isEqualTo("foofoo");
46+
LambdaAnswer<String> answer = new LambdaAnswer<>(true, (inv, result) -> result);
47+
willAnswer(answer).given(foo).foo("fail");
48+
assertThatIllegalArgumentException().isThrownBy(() -> foo.foo("fail"));
49+
Collection<Exception> exceptions = answer.getExceptions();
50+
assertThat(exceptions).hasSize(1);
51+
assertThat(exceptions.iterator().next()).isInstanceOf(IllegalArgumentException.class);
4352
}
4453

4554
private static class Foo {
@@ -49,6 +58,9 @@ private static class Foo {
4958
}
5059

5160
public String foo(String foo) {
61+
if (foo.equals("fail")) {
62+
throw new IllegalArgumentException("fail");
63+
}
5264
return foo.toUpperCase();
5365
}
5466

src/reference/asciidoc/testing.adoc

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,19 +73,9 @@ assertEquals("thingthing", thing.thing("thing"));
7373
----
7474
====
7575

76-
The following example shows how to test the `Thing` POJO with Java 7 or earlier:
76+
Starting with version 2.2.3, the answers capture any exceptions thrown by the method under test.
77+
Use `answer.getExceptions()` to get a reference to them.
7778

78-
====
79-
[source, java]
80-
----
81-
doAnswer(new LambdaAnswer<String>(true, new ValueToReturn<String>() {
82-
@Override
83-
public String apply(InvocationOnMock i, String r) {
84-
return r + r;
85-
}
86-
})).when(thing).thing(anyString());
87-
----
88-
====
8979

9080
[[test-harness]]
9181
==== `@RabbitListenerTest` and `RabbitListenerTestHarness`

0 commit comments

Comments
 (0)