Mockito Answers: Capture any exceptions

This commit is contained in:
Gary Russell
2019-12-09 12:23:09 -05:00
committed by Artem Bilan
parent e37e9aac28
commit 2874cd24c8
6 changed files with 80 additions and 23 deletions

1
.gitignore vendored
View File

@@ -19,3 +19,4 @@ erl_crash.dump
nohup.out
src/ant/.ant-targets-upload-dist.xml
target
.sts4-cache

View File

@@ -16,12 +16,17 @@
package org.springframework.amqp.rabbit.test.mockito;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
/**
* An Answer to optionally call the real method and allow returning a
* custom result.
* An {@link Answer} to optionally call the real method and allow returning a
* custom result. Captures any exceptions thrown.
*
* @author Gary Russell
* @since 1.6
@@ -33,6 +38,8 @@ public class LambdaAnswer<T> implements Answer<T> {
private final ValueToReturn<T> callback;
private final Set<Exception> exceptions = Collections.synchronizedSet(new LinkedHashSet<>());
public LambdaAnswer(boolean callRealMethod, ValueToReturn<T> callback) {
this.callRealMethod = callRealMethod;
this.callback = callback;
@@ -42,12 +49,28 @@ public class LambdaAnswer<T> implements Answer<T> {
@Override
public T answer(InvocationOnMock invocation) throws Throwable {
T result = null;
if (this.callRealMethod) {
result = (T) invocation.callRealMethod();
try {
if (this.callRealMethod) {
result = (T) invocation.callRealMethod();
}
return this.callback.apply(invocation, result);
}
catch (Exception e) {
this.exceptions.add(e);
throw e;
}
return this.callback.apply(invocation, result);
}
/**
* Return the exceptions thrown, if any.
* @return the exceptions.
* @since 2.2.3
*/
public Collection<Exception> getExceptions() {
return Collections.unmodifiableCollection(this.exceptions);
}
@FunctionalInterface
public interface ValueToReturn<T> {
T apply(InvocationOnMock invocation, T result);

View File

@@ -16,14 +16,20 @@
package org.springframework.amqp.rabbit.test.mockito;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.springframework.lang.Nullable;
/**
* An Answer for void returning methods that calls the real method and
* counts down a latch.
* An {@link Answer} for void returning methods that calls the real method and counts down
* a latch. Captures any exceptions thrown.
*
* @author Gary Russell
* @since 1.6
@@ -33,6 +39,8 @@ public class LatchCountDownAndCallRealMethodAnswer implements Answer<Void> {
private final CountDownLatch latch;
private final Set<Exception> exceptions = Collections.synchronizedSet(new LinkedHashSet<>());
/**
* @param count to set in a {@link CountDownLatch}.
*/
@@ -42,8 +50,16 @@ public class LatchCountDownAndCallRealMethodAnswer implements Answer<Void> {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
invocation.callRealMethod();
this.latch.countDown();
try {
invocation.callRealMethod();
}
catch (Exception e) {
this.exceptions.add(e);
throw e;
}
finally {
this.latch.countDown();
}
return null;
}
@@ -52,4 +68,14 @@ public class LatchCountDownAndCallRealMethodAnswer implements Answer<Void> {
return latch;
}
/**
* Return the exceptions thrown.
* @return the exceptions.
* @since 2.2.3
*/
@Nullable
public Collection<Exception> getExceptions() {
return Collections.unmodifiableCollection(this.exceptions);
}
}

View File

@@ -21,6 +21,7 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import java.util.Collection;
import java.util.concurrent.TimeUnit;
import org.junit.jupiter.api.Test;
@@ -122,6 +123,10 @@ public class ExampleRabbitListenerSpyAndCaptureTest {
assertThat((String) args[0]).isEqualTo("ex");
assertThat((String) args[1]).isEqualTo(queue2.getName());
assertThat(invocationData.getThrowable()).isNull();
Collection<Exception> exceptions = answer.getExceptions();
assertThat(exceptions).hasSize(1);
assertThat(exceptions.iterator().next()).isInstanceOf(IllegalArgumentException.class);
}
@Configuration
@@ -177,10 +182,10 @@ public class ExampleRabbitListenerSpyAndCaptureTest {
}
@RabbitListener(id = "bar", queues = "#{queue2.name}")
public void foo(@Payload String foo, @Header("amqp_receivedRoutingKey") String rk) {
public void foo(@Payload String foo, @SuppressWarnings("unused") @Header("amqp_receivedRoutingKey") String rk) {
if (!failed && foo.equals("ex")) {
failed = true;
throw new RuntimeException(foo);
throw new IllegalArgumentException(foo);
}
failed = false;
}

View File

@@ -17,10 +17,13 @@
package org.springframework.amqp.rabbit.test.mockito;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.BDDMockito.willAnswer;
import static org.mockito.Mockito.spy;
import java.util.Collection;
import org.junit.jupiter.api.Test;
/**
@@ -40,6 +43,12 @@ public class AnswerTests {
willAnswer(new LambdaAnswer<String>(false, (i, r) ->
"" + i.getArguments()[0] + i.getArguments()[0])).given(foo).foo(anyString());
assertThat(foo.foo("foo")).isEqualTo("foofoo");
LambdaAnswer<String> answer = new LambdaAnswer<>(true, (inv, result) -> result);
willAnswer(answer).given(foo).foo("fail");
assertThatIllegalArgumentException().isThrownBy(() -> foo.foo("fail"));
Collection<Exception> exceptions = answer.getExceptions();
assertThat(exceptions).hasSize(1);
assertThat(exceptions.iterator().next()).isInstanceOf(IllegalArgumentException.class);
}
private static class Foo {
@@ -49,6 +58,9 @@ public class AnswerTests {
}
public String foo(String foo) {
if (foo.equals("fail")) {
throw new IllegalArgumentException("fail");
}
return foo.toUpperCase();
}

View File

@@ -73,19 +73,9 @@ assertEquals("thingthing", thing.thing("thing"));
----
====
The following example shows how to test the `Thing` POJO with Java 7 or earlier:
Starting with version 2.2.3, the answers capture any exceptions thrown by the method under test.
Use `answer.getExceptions()` to get a reference to them.
====
[source, java]
----
doAnswer(new LambdaAnswer<String>(true, new ValueToReturn<String>() {
@Override
public String apply(InvocationOnMock i, String r) {
return r + r;
}
})).when(thing).thing(anyString());
----
====
[[test-harness]]
==== `@RabbitListenerTest` and `RabbitListenerTestHarness`