diff --git a/pom.xml b/pom.xml
index 8f7b03a78..5fd5c3d19 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,7 +9,7 @@
org.springframework.cloud
spring-cloud-build
1.1.0.BUILD-SNAPSHOT
-
+
https://github.com/spring-cloud/spring-cloud-stream
@@ -20,6 +20,7 @@
1.7
1.3.0.BUILD-SNAPSHOT
+ 4.2.1.BUILD-SNAPSHOT
4.2.0.BUILD-SNAPSHOT
1.1.0.BUILD-SNAPSHOT
@@ -30,6 +31,7 @@
spring-cloud-stream-module-launcher
spring-cloud-stream-samples
docs
+ spring-cloud-stream-test-support
@@ -67,6 +69,11 @@
spring-cloud-stream-common
${project.version}
+
+ org.springframework.cloud
+ spring-cloud-stream-binder-spi
+ ${project.version}
+
org.springframework.cloud
spring-cloud-stream-binder-local
@@ -82,11 +89,6 @@
spring-cloud-stream-binder-rabbit
${project.version}
-
- org.springframework.cloud
- spring-cloud-stream-binder-spi
- ${project.version}
-
org.springframework.cloud
spring-cloud-stream-binder-test
@@ -97,6 +99,16 @@
spring-cloud-stream-module-launcher
${project.version}
+
+ org.springframework.cloud
+ spring-cloud-stream-test-support
+ ${project.version}
+
+
+ org.springframework
+ spring-messaging
+ ${spring-framework.version}
+
org.springframework.integration
spring-integration-core
diff --git a/spring-cloud-stream-test-support/pom.xml b/spring-cloud-stream-test-support/pom.xml
new file mode 100644
index 000000000..680fce58b
--- /dev/null
+++ b/spring-cloud-stream-test-support/pom.xml
@@ -0,0 +1,41 @@
+
+
+ 4.0.0
+
+ org.springframework.cloud
+ spring-cloud-stream-parent
+ 1.0.0.BUILD-SNAPSHOT
+
+ org.springframework.cloud
+ spring-cloud-stream-test-support
+ Spring Cloud Stream Test Support
+
+ UTF-8
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+ org.springframework.cloud
+ spring-cloud-stream-binder-spi
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+ org.springframework.cloud
+ spring-cloud-stream
+ test
+
+
+
diff --git a/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/binder/MessageCollector.java b/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/binder/MessageCollector.java
new file mode 100644
index 000000000..611c5fc6f
--- /dev/null
+++ b/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/binder/MessageCollector.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright 2015 the original author or authors.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.stream.test.binder;
+
+import java.util.concurrent.BlockingQueue;
+
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageChannel;
+
+/**
+ * Maintains a map between (output) channels and messages received (in FIFO order). To be injected in tests that
+ * can then run assertions on the enqueued messages.
+ *
+ * @author Eric Bottard
+ */
+public interface MessageCollector {
+
+ /**
+ * Obtain a queue that will receive messages sent to the given channel.
+ */
+ public BlockingQueue> forChannel(MessageChannel channel);
+}
diff --git a/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/binder/TestSupportBinder.java b/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/binder/TestSupportBinder.java
new file mode 100644
index 000000000..5bc267e7e
--- /dev/null
+++ b/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/binder/TestSupportBinder.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright 2015 the original author or authors.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.stream.test.binder;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Properties;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingDeque;
+
+import org.springframework.cloud.stream.binder.Binder;
+import org.springframework.cloud.stream.test.matcher.MessageQueueMatcher;
+import org.springframework.integration.channel.QueueChannel;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.MessageChannel;
+import org.springframework.messaging.MessageHandler;
+import org.springframework.messaging.MessagingException;
+import org.springframework.messaging.SubscribableChannel;
+import org.springframework.util.Assert;
+
+/**
+ * A minimal binder that
+ * - does nothing about binding consumers, leaving the channel as-is, so that a test author can interact with it directly,
+ * - registers a queue channel on the producer side, so that it is easy to assert what is received.
+ *
+ *
+ * @author Eric Bottard
+ * @see MessageQueueMatcher
+ */
+public class TestSupportBinder implements Binder {
+
+ private final MessageCollectorImpl messageCollector = new MessageCollectorImpl();
+
+
+ @Override
+ public void bindConsumer(String name, MessageChannel inboundBindTarget, Properties properties) {
+ }
+
+ @Override
+ public void bindPubSubConsumer(String name, MessageChannel inboundBindTarget, Properties properties) {
+
+ }
+
+ /**
+ * Registers a single subscriber to the channel, that enqueues messages for later retrieval and assertion in tests.
+ */
+ @Override
+ @SuppressWarnings("unchecked")
+ public void bindProducer(String name, MessageChannel outboundBindTarget, Properties properties) {
+ final BlockingQueue queue = messageCollector.register(outboundBindTarget);
+ ((SubscribableChannel)outboundBindTarget).subscribe(new MessageHandler() {
+ @Override
+ public void handleMessage(Message> message) throws MessagingException {
+ queue.add(message);
+ }
+ });
+
+ }
+
+ @Override
+ public void unbindProducer(String name, MessageChannel channel) {
+ messageCollector.unregister(channel);
+ }
+
+ @Override
+ public void bindPubSubProducer(String name, MessageChannel outboundBindTarget, Properties properties) {
+
+ }
+
+ @Override
+ public void unbindConsumers(String name) {
+
+ }
+
+ @Override
+ public void unbindProducers(String name) {
+
+ }
+
+ @Override
+ public void unbindConsumer(String name, MessageChannel inboundBindTarget) {
+
+ }
+
+ @Override
+ public void bindRequestor(String name, MessageChannel requests, MessageChannel replies, Properties properties) {
+
+ }
+
+ @Override
+ public void bindReplier(String name, MessageChannel requests, MessageChannel replies, Properties properties) {
+
+ }
+
+ @Override
+ public MessageChannel bindDynamicProducer(String name, Properties properties) {
+ return null;
+ }
+
+ @Override
+ public MessageChannel bindDynamicPubSubProducer(String name, Properties properties) {
+ return null;
+ }
+
+ @Override
+ public boolean isCapable(Capability capability) {
+ return false;
+ }
+
+ public MessageCollector messageCollector() {
+ return messageCollector;
+ }
+
+ /**
+ * Maintains mappings between channels and queues.
+ *
+ * @author Eric Bottard
+ */
+ private static class MessageCollectorImpl implements MessageCollector{
+
+ private Map>> results = new HashMap<>();
+
+ private BlockingQueue register(MessageChannel channel) {
+ LinkedBlockingDeque> result = new LinkedBlockingDeque<>();
+ Assert.isTrue(!results.containsKey(channel), "Channel [" + channel + "] was already bound");
+ results.put(channel, result);
+ return result;
+ }
+
+ private void unregister(MessageChannel channel) {
+ Assert.notNull(results.remove(channel), "Trying to unregister a mapping for an unknown channel [" + channel + "]");
+ }
+
+ public BlockingQueue> forChannel(MessageChannel channel) {
+ BlockingQueue> queue = results.get(channel);
+ Assert.notNull(queue, "Channel [" + channel + "] was not bound by " + TestSupportBinder.class);
+ return queue;
+ }
+ }
+}
diff --git a/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/binder/TestSupportBinderAutoConfiguration.java b/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/binder/TestSupportBinderAutoConfiguration.java
new file mode 100644
index 000000000..80f029636
--- /dev/null
+++ b/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/binder/TestSupportBinderAutoConfiguration.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2015 the original author or authors.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.stream.test.binder;
+
+import org.springframework.boot.autoconfigure.AutoConfigureOrder;
+import org.springframework.cloud.stream.binder.Binder;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.Ordered;
+
+/**
+ * Installs the {@link TestSupportBinder} and exposes {@link TestSupportBinder.MessageCollectorImpl} to be injected in tests.
+ *
+ * Note that this auto-configuration has higher priority than regular binders, so adding
+ * this on the classpath in test scope is sufficient to have support kick in.
+ *
+ * @author Eric Bottard
+ */
+@Configuration
+@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
+public class TestSupportBinderAutoConfiguration {
+
+ @Bean
+ public MessageCollector messageCollector() {
+ return testSupportBinder().messageCollector();
+ }
+
+ @Bean
+ public TestSupportBinder testSupportBinder() {
+ return new TestSupportBinder();
+ }
+
+}
diff --git a/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/matcher/MessageQueueMatcher.java b/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/matcher/MessageQueueMatcher.java
new file mode 100644
index 000000000..cc84758da
--- /dev/null
+++ b/spring-cloud-stream-test-support/src/main/java/org/springframework/cloud/stream/test/matcher/MessageQueueMatcher.java
@@ -0,0 +1,160 @@
+/*
+ * Copyright 2015 the original author or authors.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.stream.test.matcher;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Matcher;
+import org.hamcrest.SelfDescribing;
+
+import org.springframework.cloud.stream.test.binder.TestSupportBinder;
+import org.springframework.integration.util.Function;
+import org.springframework.messaging.Message;
+
+/**
+ * A Hamcrest Matcher meant to be used in conjunction with {@link TestSupportBinder}.
+ *
+ * Expected usage is of the form (with appropriate static imports):
+ *
+ * public class TransformProcessorApplicationTests {
+ *
+ * {@literal @}Autowired
+ * {@literal @}ModuleChannels(TransformProcessor.class)
+ * private Processor processor;
+ *
+ * {@literal @}Autowired
+ * private MessageCollectorImpl messageCollector;
+ *
+ *
+ * {@literal @}Test
+ * public void testUsingExpression() {
+ * processor.input().send(new GenericMessage{@literal <}Object>("hello"));
+ * assertThat(messageCollector.forChannel(processor.output()), receivesPayloadThat(is("hellofoo")).within(10));
+ * }
+ *
+ * }
+ *
+ *
+ * @author Eric Bottard
+ */
+public class MessageQueueMatcher extends BaseMatcher>> {
+
+ private final Matcher delegate;
+
+ private final long timeout;
+
+ private Extractor, T> extractor;
+
+ private Map>, T> actuallyReceived = new HashMap<>();
+
+ private final TimeUnit unit;
+
+ public MessageQueueMatcher(Matcher delegate, long timeout, TimeUnit unit, Extractor, T> extractor) {
+ this.delegate = delegate;
+ this.timeout = timeout;
+ this.unit = unit;
+ this.extractor = extractor;
+ }
+
+
+ @Override
+ public boolean matches(Object item) {
+ @SuppressWarnings("unchecked")
+ BlockingQueue> queue = (BlockingQueue>) item;
+ Message> received = null;
+ try {
+ if (timeout > 0) {
+ received = queue.poll(timeout, unit);
+ } else if (timeout == 0) {
+ received = queue.poll();
+ } else {
+ received = queue.take();
+ }
+ }
+ catch (InterruptedException e) {
+ return false;
+ }
+ T unwrapped = extractor.apply(received);
+ actuallyReceived.put(queue, unwrapped);
+ return delegate.matches(unwrapped);
+ }
+
+ @Override
+ public void describeMismatch(Object item, Description description) {
+ @SuppressWarnings("unchecked")
+ BlockingQueue> queue = (BlockingQueue>) item;
+ T value = actuallyReceived.get(queue);
+ if (value != null) {
+ description.appendText("received: ").appendValue(value);
+ } else {
+ description.appendText("timed out after " + timeout + " " + unit.name().toLowerCase());
+ }
+ }
+
+ public MessageQueueMatcher within(long timeout, TimeUnit unit) {
+ return new MessageQueueMatcher<>(this.delegate, timeout, unit, this.extractor);
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText("Channel to receive ").appendDescriptionOf(extractor).appendDescriptionOf(delegate);
+ }
+
+ @SuppressWarnings("unchecked")
+ public static MessageQueueMatcher
receivesMessageThat(Matcher> messageMatcher) {
+ return new MessageQueueMatcher(messageMatcher, -1, null, new Extractor, Message>("a message that ") {
+ @Override
+ public Message
apply(Message
m) {
+ return m;
+ }
+ });
+ }
+
+ @SuppressWarnings("unchecked")
+ public static
MessageQueueMatcher
receivesPayloadThat(Matcher
payloadMatcher) {
+ return new MessageQueueMatcher(payloadMatcher, -1, null, new Extractor, P>("a message whose payload ") {
+ @Override
+ public P apply(Message m) {
+ return m.getPayload();
+ }
+ });
+ }
+
+ /**
+ * A transformation to be applied to a received message before asserting, e.g. to only inspect the payload.
+ */
+ public static abstract class Extractor implements Function, SelfDescribing {
+
+ private final String behaviorDescription;
+
+ protected Extractor(String behaviorDescription) {
+ this.behaviorDescription = behaviorDescription;
+ }
+
+ @Override
+ public void describeTo(Description description) {
+ description.appendText(behaviorDescription);
+ }
+ }
+
+
+
+}
diff --git a/spring-cloud-stream-test-support/src/main/resources/META-INF/spring.factories b/spring-cloud-stream-test-support/src/main/resources/META-INF/spring.factories
new file mode 100644
index 000000000..baa0d75ca
--- /dev/null
+++ b/spring-cloud-stream-test-support/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration:\
+org.springframework.cloud.stream.test.binder.TestSupportBinderAutoConfiguration
diff --git a/spring-cloud-stream-test-support/src/test/java/org/springframework/cloud/stream/test/ExampleTest.java b/spring-cloud-stream-test-support/src/test/java/org/springframework/cloud/stream/test/ExampleTest.java
new file mode 100644
index 000000000..af2ad41be
--- /dev/null
+++ b/spring-cloud-stream-test-support/src/test/java/org/springframework/cloud/stream/test/ExampleTest.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2015 the original author or authors.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.stream.test;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.test.IntegrationTest;
+import org.springframework.boot.test.SpringApplicationConfiguration;
+import org.springframework.cloud.stream.annotation.EnableModule;
+import org.springframework.cloud.stream.annotation.ModuleChannels;
+import org.springframework.cloud.stream.annotation.Processor;
+import org.springframework.cloud.stream.test.binder.MessageCollector;
+import org.springframework.cloud.stream.test.binder.TestSupportBinder;
+import org.springframework.integration.annotation.Transformer;
+import org.springframework.messaging.Message;
+import org.springframework.messaging.support.GenericMessage;
+import org.springframework.test.annotation.DirtiesContext;
+import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
+
+/**
+ * Integration test that validates that {@link org.springframework.cloud.stream.test.binder.TestSupportBinder} applies
+ * correctly.
+ */
+@RunWith(SpringJUnit4ClassRunner.class)
+@SpringApplicationConfiguration(classes = ExampleTest.MyProcessor.class)
+@IntegrationTest({"server.port=-1"})
+@DirtiesContext
+public class ExampleTest {
+
+ @Autowired
+ @ModuleChannels(MyProcessor.class)
+ private Processor processor;
+
+ @Autowired
+ private MessageCollector messageCollector;
+
+ @Test
+ @SuppressWarnings("unchecked")
+ public void testWiring() {
+ Message message = new GenericMessage<>("hello");
+ processor.input().send(message);
+ Message received = (Message) messageCollector.forChannel(processor.output()).poll();
+ assertThat(received.getPayload(), equalTo("hello world"));
+ }
+
+
+ @SpringBootApplication
+ @EnableModule(Processor.class)
+ public static class MyProcessor {
+
+ @Autowired
+ private Processor channels;
+
+ @Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
+ public String transform(String in) {
+ return in + " world";
+ }
+ }
+
+}
diff --git a/spring-cloud-stream-test-support/src/test/java/org/springframework/cloud/stream/test/matcher/MessageQueueMatcherTest.java b/spring-cloud-stream-test-support/src/test/java/org/springframework/cloud/stream/test/matcher/MessageQueueMatcherTest.java
new file mode 100644
index 000000000..7d056e5a3
--- /dev/null
+++ b/spring-cloud-stream-test-support/src/test/java/org/springframework/cloud/stream/test/matcher/MessageQueueMatcherTest.java
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2015 the original author or authors.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.springframework.cloud.stream.test.matcher;
+
+import static org.hamcrest.CoreMatchers.*;
+import static org.junit.Assert.*;
+
+import java.util.Collections;
+import java.util.concurrent.BlockingDeque;
+import java.util.concurrent.LinkedBlockingDeque;
+import java.util.concurrent.TimeUnit;
+
+import org.hamcrest.StringDescription;
+import org.junit.Test;
+
+import org.springframework.messaging.Message;
+import org.springframework.messaging.support.GenericMessage;
+
+/**
+ * Tests for MessageQueueMatcher.
+ *
+ * @author Eric Bottard
+ */
+public class MessageQueueMatcherTest {
+
+ private final BlockingDeque> queue = new LinkedBlockingDeque<>();
+
+ private final StringDescription description = new StringDescription();
+
+ @Test
+ public void testTimeout() {
+ Message> msg = new GenericMessage<>("hello");
+ MessageQueueMatcher> matcher = MessageQueueMatcher.receivesMessageThat(is(msg)).within(2, TimeUnit.MILLISECONDS);
+
+ boolean result = matcher.matches(queue);
+ assertThat(result, is(false));
+ matcher.describeMismatch(queue, description);
+ assertThat(description.toString(), is("timed out after 2 milliseconds"));
+ }
+
+ @Test
+ public void testMatch() {
+ Message> msg = new GenericMessage<>("hello");
+ MessageQueueMatcher> matcher = MessageQueueMatcher.receivesMessageThat(is(msg));
+
+ queue.offer(msg);
+
+ boolean result = matcher.matches(queue);
+ assertThat(result, is(true));
+ }
+
+ @Test
+ public void testMisMatch() {
+ Message> msg = new GenericMessage<>("hello");
+ Message> other = new GenericMessage<>("world");
+
+ MessageQueueMatcher> matcher = MessageQueueMatcher.receivesMessageThat(is(msg));
+
+ queue.offer(other);
+
+ boolean result = matcher.matches(queue);
+ assertThat(result, is(false));
+ matcher.describeMismatch(queue, description);
+ assertThat(description.toString(), is("received: <" + other + ">"));
+ }
+
+ @Test
+ public void testExtractor() {
+ Message> msg = new GenericMessage<>("hello", Collections.singletonMap("foo", (Object) "bar"));
+
+ MessageQueueMatcher.Extractor, String> headerExtractor = new MessageQueueMatcher.Extractor, String>("whose 'foo' header") {
+ @Override
+ public String apply(Message> message) {
+ return message.getHeaders().get("foo", String.class);
+ }
+ };
+
+ MessageQueueMatcher> matcher = new MessageQueueMatcher<>(is("bar"), -1, null, headerExtractor);
+ queue.offer(msg);
+ boolean result = matcher.matches(queue);
+ assertThat(result, is(true));
+
+ matcher = new MessageQueueMatcher<>(is("wizz"), -1, null, headerExtractor);
+ queue.offer(msg);
+ result = matcher.matches(queue);
+ assertThat(result, is(false));
+ matcher.describeMismatch(queue, description);
+ assertThat(description.toString(), is("received: \"bar\""));
+ }
+
+ @Test
+ public void testDescription() {
+ Message> msg = new GenericMessage<>("hello");
+
+ MessageQueueMatcher> matcher = MessageQueueMatcher.receivesMessageThat(is(msg));
+
+ description.appendDescriptionOf(matcher);
+ assertThat(description.toString(), is("Channel to receive a message that is <" + msg + ">"));
+ }
+}