Stub Runner has the functionality to run the published stubs in memory. It can integrate with the following frameworks out of the box
It also provides points of entry to integrate with any other solution on the market.
![]() | Important |
|---|---|
If you have multiple frameworks on the classpath Stub Runner will need to
define which one should be used. Let’s assume that you have both AMQP, Spring Cloud Stream and Spring Integration
on the classpath. Then you need to set |
To trigger a message it’s enough to use the StubTrigger interface:
package org.springframework.cloud.contract.stubrunner; import java.util.Collection; import java.util.Map; public interface StubTrigger { /** * Triggers an event by a given label for a given {@code groupid:artifactid} notation. You can use only {@code artifactId} too. * * Feature related to messaging. * * @return true - if managed to run a trigger */ boolean trigger(String ivyNotation, String labelName); /** * Triggers an event by a given label. * * Feature related to messaging. * * @return true - if managed to run a trigger */ boolean trigger(String labelName); /** * Triggers all possible events. * * Feature related to messaging. * * @return true - if managed to run a trigger */ boolean trigger(); /** * Returns a mapping of ivy notation of a dependency to all the labels it has. * * Feature related to messaging. */ Map<String, Collection<String>> labels(); }
For convenience the StubFinder interface extends StubTrigger so it’s enough to use only one in your tests.
StubTrigger gives you the following options to trigger a message:
stubFinder.trigger('org.springframework.cloud.contract.verifier.stubs:camelService', 'return_book_1')
Spring Cloud Contract Verifier Stub Runner’s messaging module gives you an easy way to integrate with Apache Camel. For the provided artifacts it will automatically download the stubs and register the required routes.
It’s enough to have both Apache Camel and Spring Cloud Contract Stub Runner on classpath.
Remember to annotate your test class with @AutoConfigureStubRunner.
If you need to disable this functionality just pass stubrunner.camel.enabled=false property.
Let us assume that we have the following Maven repository with a deployed stubs for the
camelService application.
└── .m2
└── repository
└── io
└── codearte
└── accurest
└── stubs
└── camelService
├── 0.0.1-SNAPSHOT
│ ├── camelService-0.0.1-SNAPSHOT.pom
│ ├── camelService-0.0.1-SNAPSHOT-stubs.jar
│ └── maven-metadata-local.xml
└── maven-metadata-local.xmlAnd the stubs contain the following structure:
├── META-INF
│ └── MANIFEST.MF
└── repository
├── accurest
│ ├── bookDeleted.groovy
│ ├── bookReturned1.groovy
│ └── bookReturned2.groovy
└── mappingsLet’s consider the following contracts (let' number it with 1):
Contract.make {
label 'return_book_1'
input {
triggeredBy('bookReturnedTriggered()')
}
outputMessage {
sentTo('jms:output')
body('''{ "bookName" : "foo" }''')
headers {
header('BOOK-NAME', 'foo')
}
}
}and number 2
Contract.make {
label 'return_book_2'
input {
messageFrom('jms:input')
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
}
outputMessage {
sentTo('jms:output')
body([
bookName: 'foo'
])
headers {
header('BOOK-NAME', 'foo')
}
}
}So as to trigger a message via the return_book_1 label we’ll use the StubTigger interface as follows
stubFinder.trigger('return_book_1')Next we’ll want to listen to the output of the message sent to jms:output
Exchange receivedMessage = camelContext.createConsumerTemplate().receive('jms:output', 5000)
And the received message would pass the following assertions
receivedMessage != null assertThatBodyContainsBookNameFoo(receivedMessage.in.body) receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
Since the route is set for you it’s enough to just send a message to the jms:output destination.
camelContext.createProducerTemplate().sendBodyAndHeaders('jms:input', new BookReturned('foo'), [sample: 'header'])
Next we’ll want to listen to the output of the message sent to jms:output
Exchange receivedMessage = camelContext.createConsumerTemplate().receive('jms:output', 5000)
And the received message would pass the following assertions
receivedMessage != null assertThatBodyContainsBookNameFoo(receivedMessage.in.body) receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
Spring Cloud Contract Verifier Stub Runner’s messaging module gives you an easy way to integrate with Spring Integration. For the provided artifacts it will automatically download the stubs and register the required routes.
It’s enough to have both Spring Integration and Spring Cloud Contract Stub Runner on classpath.
Remember to annotate your test class with @AutoConfigureStubRunner.
If you need to disable this functionality just pass stubrunner.integration.enabled=false property.
Let us assume that we have the following Maven repository with a deployed stubs for the
integrationService application.
└── .m2
└── repository
└── io
└── codearte
└── accurest
└── stubs
└── integrationService
├── 0.0.1-SNAPSHOT
│ ├── integrationService-0.0.1-SNAPSHOT.pom
│ ├── integrationService-0.0.1-SNAPSHOT-stubs.jar
│ └── maven-metadata-local.xml
└── maven-metadata-local.xmlAnd the stubs contain the following structure:
├── META-INF
│ └── MANIFEST.MF
└── repository
├── accurest
│ ├── bookDeleted.groovy
│ ├── bookReturned1.groovy
│ └── bookReturned2.groovy
└── mappingsLet’s consider the following contracts (let' number it with 1):
Contract.make {
label 'return_book_1'
input {
triggeredBy('bookReturnedTriggered()')
}
outputMessage {
sentTo('output')
body('''{ "bookName" : "foo" }''')
headers {
header('BOOK-NAME', 'foo')
}
}
}and number 2
Contract.make {
label 'return_book_2'
input {
messageFrom('input')
messageBody([
bookName: 'foo'
])
messageHeaders {
header('sample', 'header')
}
}
outputMessage {
sentTo('output')
body([
bookName: 'foo'
])
headers {
header('BOOK-NAME', 'foo')
}
}
}and the following Spring Integration Route:
<?xml version="1.0" encoding="UTF-8"?> <beans:beans xmlns="http://www.springframework.org/schema/integration" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/integration https://www.springframework.org/schema/integration/spring-integration.xsd"> <!-- REQUIRED FOR TESTING --> <bridge input-channel="output" output-channel="outputTest"/> <channel id="outputTest"> <queue/> </channel> </beans:beans>
So as to trigger a message via the return_book_1 label we’ll use the StubTigger interface as follows
stubFinder.trigger('return_book_1')Next we’ll want to listen to the output of the message sent to output
Message<?> receivedMessage = messaging.receive('outputTest')And the received message would pass the following assertions
receivedMessage != null assertJsons(receivedMessage.payload) receivedMessage.headers.get('BOOK-NAME') == 'foo'
Since the route is set for you it’s enough to just send a message to the output destination.
messaging.send(new BookReturned('foo'), [sample: 'header'], 'input')
Next we’ll want to listen to the output of the message sent to output
Message<?> receivedMessage = messaging.receive('outputTest')And the received message would pass the following assertions
receivedMessage != null assertJsons(receivedMessage.payload) receivedMessage.headers.get('BOOK-NAME') == 'foo'
Spring Cloud Contract Verifier Stub Runner’s messaging module gives you an easy way to integrate with Spring Stream. For the provided artifacts it will automatically download the stubs and register the required routes.
![]() | Warning |
|---|---|
In Stub Runner’s integration with Stream the |
![]() | Important |
|---|---|
If you want to use Spring Cloud Stream remember to add a
|
Maven.
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream-test-support</artifactId> <scope>test</scope> </dependency>
Gradle.
testCompile "org.springframework.cloud:spring-cloud-stream-test-support"
It’s enough to have both Spring Cloud Stream and Spring Cloud Contract Stub Runner on classpath.
Remember to annotate your test class with @AutoConfigureStubRunner.
If you need to disable this functionality just pass stubrunner.stream.enabled=false property.
Let us assume that we have the following Maven repository with a deployed stubs for the
streamService application.
└── .m2
└── repository
└── io
└── codearte
└── accurest
└── stubs
└── streamService
├── 0.0.1-SNAPSHOT
│ ├── streamService-0.0.1-SNAPSHOT.pom
│ ├── streamService-0.0.1-SNAPSHOT-stubs.jar
│ └── maven-metadata-local.xml
└── maven-metadata-local.xmlAnd the stubs contain the following structure:
├── META-INF
│ └── MANIFEST.MF
└── repository
├── accurest
│ ├── bookDeleted.groovy
│ ├── bookReturned1.groovy
│ └── bookReturned2.groovy
└── mappingsLet’s consider the following contracts (let' number it with 1):
Contract.make {
label 'return_book_1'
input { triggeredBy('bookReturnedTriggered()') }
outputMessage {
sentTo('returnBook')
body('''{ "bookName" : "foo" }''')
headers { header('BOOK-NAME', 'foo') }
}
}and number 2
Contract.make {
label 'return_book_2'
input {
messageFrom('bookStorage')
messageBody([
bookName: 'foo'
])
messageHeaders { header('sample', 'header') }
}
outputMessage {
sentTo('returnBook')
body([
bookName: 'foo'
])
headers { header('BOOK-NAME', 'foo') }
}
}and the following Spring configuration:
stubrunner.repositoryRoot: classpath:m2repo/repository/ stubrunner.ids: org.springframework.cloud.contract.verifier.stubs:streamService:0.0.1-SNAPSHOT:stubs spring: cloud: stream: bindings: output: destination: returnBook input: destination: bookStorage server: port: 0 debug: true
So as to trigger a message via the return_book_1 label we’ll use the StubTrigger interface as follows
stubFinder.trigger('return_book_1')Next we’ll want to listen to the output of the message sent to a channel whose destination is returnBook
Message<?> receivedMessage = messaging.receive('returnBook')And the received message would pass the following assertions
receivedMessage != null assertJsons(receivedMessage.payload) receivedMessage.headers.get('BOOK-NAME') == 'foo'
Since the route is set for you it’s enough to just send a message to the bookStorage destination.
messaging.send(new BookReturned('foo'), [sample: 'header'], 'bookStorage')
Next we’ll want to listen to the output of the message sent to returnBook
Message<?> receivedMessage = messaging.receive('returnBook')And the received message would pass the following assertions
receivedMessage != null assertJsons(receivedMessage.payload) receivedMessage.headers.get('BOOK-NAME') == 'foo'
Spring Cloud Contract Verifier Stub Runner’s messaging module provides an easy way to integrate with Spring AMQP’s Rabbit Template. For the provided artifacts it will automatically download the stubs and register the required routes.
The integration tries to work standalone, that is without interaction with a running RabbitMQ message broker.
It expects a RabbitTemplate on the application context and uses it as a spring boot test @SpyBean.
Thus it can use the mockito spy functionality to verify and introspect messages sent by the application.
On the message consumer side, it considers all @RabbitListener annotated endpoints as well as all `SimpleMessageListenerContainer`s on the application context.
As messages are usually sent to exchanges in AMQP the message contract contains the exchange name as the destination. Message listeners on the other side are bound to queues. Bindings connect an exchange to a queue. If message contracts are triggered the Spring AMQP stub runner integration will look for bindings on the application context that match this exchange. Then it collects the queues from the Spring exchanges and tries to find messages listeners bound to these queues. The message is triggered to all matching message listeners.
It’s enough to have both Spring AMQP and Spring Cloud Contract Stub Runner on the classpath and set the property stubrunner.amqp.enabled=true.
Remember to annotate your test class with @AutoConfigureStubRunner.
![]() | Important |
|---|---|
If you already have Stream and Integration on the classpath you need
to disable them explicitly via |
Let us assume that we have the following Maven repository with a deployed stubs for the
spring-cloud-contract-amqp-test application.
└── .m2
└── repository
└── com
└── example
└── spring-cloud-contract-amqp-test
├── 0.4.0-SNAPSHOT
│ ├── spring-cloud-contract-amqp-test-0.4.0-SNAPSHOT.pom
│ ├── spring-cloud-contract-amqp-test-0.4.0-SNAPSHOT-stubs.jar
│ └── maven-metadata-local.xml
└── maven-metadata-local.xmlAnd the stubs contain the following structure:
├── META-INF
│ └── MANIFEST.MF
└── contracts
└── shouldProduceValidPersonData.groovyLet’s consider the following contract:
Contract.make {
// Human readable description
description 'Should produce valid person data'
// Label by means of which the output message can be triggered
label 'contract-test.person.created.event'
// input to the contract
input {
// the contract will be triggered by a method
triggeredBy('createPerson()')
}
// output message of the contract
outputMessage {
// destination to which the output message will be sent
sentTo 'contract-test.exchange'
headers {
header('contentType': 'application/json')
header('__TypeId__': 'org.springframework.cloud.contract.stubrunner.messaging.amqp.Person')
}
// the body of the output message
body ([
id: $(consumer(9), producer(regex("[0-9]+"))),
name: "me"
])
}
}and the following Spring configuration:
stubrunner: repositoryRoot: classpath:m2repo/repository/ ids: org.springframework.cloud.contract.verifier.stubs.amqp:spring-cloud-contract-amqp-test:0.4.0-SNAPSHOT:stubs amqp: enabled: true server: port: 0
So to trigger a message using the contract above we’ll use the StubTrigger interface as follows.
stubTrigger.trigger("contract-test.person.created.event")The message has the destination contract-test.exchange so the Spring AMQP stub runner integration looks for bindings related to this exchange.
@Bean public Binding binding() { return BindingBuilder.bind(new Queue("test.queue")).to(new DirectExchange("contract-test.exchange")).with("#"); }
The binding definition binds the queue test.queue.
So the following listener definition is a match and is invoked with the contract message.
@Bean public SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory, MessageListenerAdapter listenerAdapter) { SimpleMessageListenerContainer container = new SimpleMessageListenerContainer(); container.setConnectionFactory(connectionFactory); container.setQueueNames("test.queue"); container.setMessageListener(listenerAdapter); return container; }
Also, the following annotated listener represents a match and would be invoked.
@RabbitListener(bindings = @QueueBinding( value = @Queue(value = "test.queue"), exchange = @Exchange(value = "contract-test.exchange", ignoreDeclarationExceptions = "true"))) public void handlePerson(Person person) { this.person = person; }
![]() | Note |
|---|---|
The message is directly handed over to the |