Should work

This commit is contained in:
Marcin Grzejszczak
2020-01-29 19:42:14 +01:00
parent 704be942cf
commit 403d369b44
16 changed files with 158 additions and 159 deletions

View File

@@ -24,7 +24,6 @@ import org.springframework.context.annotation.Configuration;
@Configuration
@SpringBootApplication
@EnableBinding({ Source.class, MyProcessor.class })
public class Application {
public static void main(String[] args) {

View File

@@ -36,7 +36,7 @@ import org.springframework.cloud.contract.spec.Contract;
import org.springframework.cloud.contract.stubrunner.BatchStubRunner;
import org.springframework.cloud.contract.stubrunner.StubConfiguration;
import org.springframework.cloud.contract.stubrunner.messaging.integration.StubRunnerIntegrationConfiguration;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.binder.test.InputDestination;
import org.springframework.cloud.stream.config.BindingProperties;
import org.springframework.cloud.stream.config.BindingServiceProperties;
import org.springframework.context.Lifecycle;
@@ -56,7 +56,7 @@ import org.springframework.util.StringUtils;
* @author Marcin Grzejszczak
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ IntegrationFlows.class, EnableBinding.class })
@ConditionalOnClass({ IntegrationFlows.class, InputDestination.class })
@ConditionalOnProperty(name = "stubrunner.stream.enabled", havingValue = "true",
matchIfMissing = true)
@AutoConfigureBefore(StubRunnerIntegrationConfiguration.class)

View File

@@ -16,10 +16,13 @@
package org.springframework.cloud.contract.stubrunner.spring.cloud
import java.util.function.Function
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.client.TestRestTemplate
@@ -27,15 +30,14 @@ import org.springframework.cloud.contract.stubrunner.StubFinder
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier
import org.springframework.cloud.stream.annotation.EnableBinding
import org.springframework.cloud.stream.messaging.Sink
import org.springframework.cloud.stream.messaging.Source
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.env.Environment
import org.springframework.http.ResponseEntity
import org.springframework.messaging.Message
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
/**
* @author Marcin Grzejszczak
*/
@@ -46,6 +48,7 @@ import org.springframework.test.context.ContextConfiguration
repositoryRoot = "classpath:m2repo/repository/",
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
stubsPerConsumer = true)
@ActiveProfiles("streamconsumer")
class StubRunnerStubsPerConsumerSpec extends Specification {
// end::test[]
@@ -77,7 +80,7 @@ class StubRunnerStubsPerConsumerSpec extends Specification {
Message<?> receivedMessage = messaging.receive('output')
and:
receivedMessage != null
receivedMessage.payload == '''{"bookName":"foo_for_bar"}'''
receivedMessage.payload == '''{"bookName":"foo_for_bar"}'''.bytes
receivedMessage.headers.get('BOOK-NAME') == 'foo_for_bar'
}
@@ -91,6 +94,14 @@ class StubRunnerStubsPerConsumerSpec extends Specification {
@Configuration
@EnableAutoConfiguration
@EnableBinding([Sink, Source])
static class Config {}
@ImportAutoConfiguration(TestChannelBinderConfiguration.class)
static class Config {
@Bean
Function output() {
return { Object o ->
println(o)
return o
}
}
}
}

View File

@@ -16,10 +16,13 @@
package org.springframework.cloud.contract.stubrunner.spring.cloud
import java.util.function.Function
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.boot.test.web.client.TestRestTemplate
@@ -27,15 +30,14 @@ import org.springframework.cloud.contract.stubrunner.StubFinder
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier
import org.springframework.cloud.stream.annotation.EnableBinding
import org.springframework.cloud.stream.messaging.Sink
import org.springframework.cloud.stream.messaging.Source
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.core.env.Environment
import org.springframework.http.ResponseEntity
import org.springframework.messaging.Message
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
/**
* @author Marcin Grzejszczak
*/
@@ -47,6 +49,7 @@ import org.springframework.test.context.ContextConfiguration
consumerName = "foo-consumer",
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
stubsPerConsumer = true)
@ActiveProfiles("streamconsumer")
class StubRunnerStubsPerConsumerWithConsumerNameSpec extends Specification {
// end::test[]
@@ -56,6 +59,7 @@ class StubRunnerStubsPerConsumerWithConsumerNameSpec extends Specification {
Environment environment
@Autowired
MessageVerifier<Message<?>> messaging
TestRestTemplate template = new TestRestTemplate()
def 'should start http stub servers for foo-consumer only'() {
@@ -78,7 +82,7 @@ class StubRunnerStubsPerConsumerWithConsumerNameSpec extends Specification {
Message<?> receivedMessage = messaging.receive('output')
and:
receivedMessage != null
receivedMessage.payload == '''{"bookName":"foo_for_foo"}'''
receivedMessage.payload == '''{"bookName":"foo_for_foo"}'''.bytes
receivedMessage.headers.get('BOOK-NAME') == 'foo_for_foo'
}
@@ -92,6 +96,14 @@ class StubRunnerStubsPerConsumerWithConsumerNameSpec extends Specification {
@Configuration
@EnableAutoConfiguration
@EnableBinding([Sink, Source])
static class Config {}
@ImportAutoConfiguration(TestChannelBinderConfiguration.class)
static class Config {
@Bean
Function output() {
return { Object o ->
println(o)
return o
}
}
}
}

View File

@@ -0,0 +1,5 @@
spring.cloud.function.definition: output
spring.cloud.stream.bindings.output-in-0.destination: output
spring.cloud.stream.bindings.output-in-0.contentType: application/json
spring.cloud.stream.bindings.output-out-0.destination: output
spring.cloud.stream.bindings.output-out-0.contentType: application/json

View File

@@ -16,19 +16,15 @@
package org.springframework.cloud.contract.verifier.messaging.stream;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.contract.verifier.messaging.MessageVerifierSender;
import org.springframework.cloud.stream.binder.test.InputDestination;
import org.springframework.cloud.stream.function.StreamFunctionProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.messaging.Message;
import org.springframework.util.StringUtils;
/**
* @author Marcin Grzejszczak
@@ -56,12 +52,7 @@ class StreamInputDestinationMessageSender implements MessageVerifierSender<Messa
try {
InputDestination inputDestination = this.context
.getBean(InputDestination.class);
StreamFunctionProperties streamFunctionProperties = this.context
.getBean(StreamFunctionProperties.class);
int indexOfDestination = StringUtils
.isEmpty(streamFunctionProperties.getDefinition()) ? 0
: indexOfDestination(streamFunctionProperties, destination);
inputDestination.send(message, indexOfDestination);
inputDestination.send(message, destination);
}
catch (Exception e) {
log.error("Exception occurred while trying to send a message [" + message
@@ -70,17 +61,4 @@ class StreamInputDestinationMessageSender implements MessageVerifierSender<Messa
}
}
private int indexOfDestination(StreamFunctionProperties streamFunctionProperties,
String destination) {
String[] split = streamFunctionProperties.getDefinition().split(";");
int indexOfDestination = Arrays.stream(split).map(String::toLowerCase)
.collect(Collectors.toList()).indexOf(destination.toLowerCase());
if (indexOfDestination == -1) {
throw new IllegalStateException("Destination with name [" + destination
+ "] not found in the function definitions ["
+ streamFunctionProperties.getDefinition() + "]");
}
return indexOfDestination;
}
}

View File

@@ -16,19 +16,15 @@
package org.springframework.cloud.contract.verifier.messaging.stream;
import java.util.Arrays;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.cloud.contract.verifier.messaging.MessageVerifierReceiver;
import org.springframework.cloud.stream.binder.test.OutputDestination;
import org.springframework.cloud.stream.function.StreamFunctionProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.messaging.Message;
import org.springframework.util.StringUtils;
class StreamOutputDestinationMessageReceiver
implements MessageVerifierReceiver<Message<?>> {
@@ -47,13 +43,8 @@ class StreamOutputDestinationMessageReceiver
try {
OutputDestination outputDestination = this.context
.getBean(OutputDestination.class);
StreamFunctionProperties streamFunctionProperties = this.context
.getBean(StreamFunctionProperties.class);
int indexOfDestination = StringUtils
.isEmpty(streamFunctionProperties.getDefinition()) ? 0
: indexOfDestination(streamFunctionProperties, destination);
return outputDestination.receive(timeUnit.toMillis(timeout),
indexOfDestination);
destination);
}
catch (Exception e) {
log.error("Exception occurred while trying to read a message from "
@@ -62,22 +53,6 @@ class StreamOutputDestinationMessageReceiver
}
}
private int indexOfDestination(StreamFunctionProperties streamFunctionProperties,
String destination) {
String[] split = streamFunctionProperties.getDefinition().split(";");
if (split.length == 1) {
return 0;
}
int indexOfDestination = Arrays.stream(split).map(String::toLowerCase)
.collect(Collectors.toList()).indexOf(destination.toLowerCase());
if (indexOfDestination == -1) {
throw new IllegalStateException("Destination with name [" + destination
+ "] not found in the function definitions ["
+ streamFunctionProperties.getDefinition() + "]");
}
return indexOfDestination;
}
@Override
public Message<?> receive(String destination) {
return receive(destination, 5, TimeUnit.SECONDS);

View File

@@ -17,48 +17,28 @@
package com.example;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.function.Consumer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
@Component
public class BookListener {
@Component("bookDeleted")
public class BookDeletedListener implements Consumer<BookDeleted> {
private static final Logger log = LoggerFactory.getLogger(BookListener.class);
private static final Logger log = LoggerFactory.getLogger(BookDeletedListener.class);
public AtomicBoolean bookSuccessfulyDeleted = new AtomicBoolean(false);
@Autowired
private Source source;
/**
* Scenario for "should generate tests triggered by a message": client side: if sends
* a message to input.messageFrom then message will be sent to output.messageFrom
* server side: will send a message to input, verify the message contents and await
* upon receiving message on the output messageFrom
*/
@StreamListener(Sink.INPUT)
public void returnBook(BookReturned bookReturned) {
log.info("Returning book " + bookReturned);
this.source.output().send(MessageBuilder.withPayload(bookReturned)
.setHeader("BOOK-NAME", bookReturned.bookName).build());
}
/**
* Scenario for "should generate tests triggered by a message": client side: if sends
* a message to input.messageFrom then message will be sent to output.messageFrom
* server side: will send a message to input, verify the message contents and await
* upon receiving message on the output messageFrom
*/
@StreamListener(DeleteSink.MYINPUT)
public void bookDeleted(BookDeleted bookDeleted) {
@Override
public void accept(BookDeleted bookDeleted) {
log.info("Deleting book " + bookDeleted);
// ... doing some work
this.bookSuccessfulyDeleted.set(true);

View File

@@ -0,0 +1,47 @@
/*
* Copyright 2013-2020 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
*
* https://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 com.example;
import java.util.function.Function;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;
@Component("bookReturned")
public class BookReturnedListener
implements Function<BookReturned, Message<BookReturned>> {
private static final Logger log = LoggerFactory.getLogger(BookReturnedListener.class);
/**
* Scenario for "should generate tests triggered by a message": client side: if sends
* a message to input.messageFrom then message will be sent to output.messageFrom
* server side: will send a message to input, verify the message contents and await
* upon receiving message on the output messageFrom
*/
@Override
public Message<BookReturned> apply(BookReturned bookReturned) {
log.info("Got the book " + bookReturned);
return MessageBuilder.withPayload(bookReturned)
.setHeader("BOOK-NAME", bookReturned.bookName).build();
}
}

View File

@@ -16,24 +16,27 @@
package com.example;
import java.util.function.Supplier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import reactor.core.publisher.EmitterProcessor;
import reactor.core.publisher.Flux;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Service;
@Service
public class BookService {
@Service("bookSender")
public class BookService implements Supplier<Flux<Message<BookReturned>>> {
private static final Logger log = LoggerFactory.getLogger(BookService.class);
private final Source source;
private final EmitterProcessor<Message<BookReturned>> bookReturnedEmitterProcessor;
@Autowired
public BookService(Source source) {
this.source = source;
public BookService(
EmitterProcessor<Message<BookReturned>> bookReturnedEmitterProcessor) {
this.bookReturnedEmitterProcessor = bookReturnedEmitterProcessor;
}
/**
@@ -46,8 +49,13 @@ public class BookService {
*/
public void returnBook(BookReturned bookReturned) {
log.info("Returning book " + bookReturned);
this.source.output().send(MessageBuilder.withPayload(bookReturned)
this.bookReturnedEmitterProcessor.onNext(MessageBuilder.withPayload(bookReturned)
.setHeader("BOOK-NAME", bookReturned.bookName).build());
}
@Override
public Flux<Message<BookReturned>> get() {
return this.bookReturnedEmitterProcessor;
}
}

View File

@@ -1,32 +0,0 @@
/*
* Copyright 2013-2020 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
*
* https://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 com.example;
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.messaging.SubscribableChannel;
/**
* @author Marcin Grzejszczak
*/
interface DeleteSink {
String MYINPUT = "delete";
@Input(DeleteSink.MYINPUT)
SubscribableChannel delete();
}

View File

@@ -16,18 +16,23 @@
package com.example;
import reactor.core.publisher.EmitterProcessor;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.context.annotation.Bean;
import org.springframework.messaging.Message;
@SpringBootApplication
@EnableBinding({ Source.class, DeleteSink.class, Sink.class })
class StreamMessagingApplication {
public static void main(String[] args) {
SpringApplication.run(StreamMessagingApplication.class, args);
}
@Bean
EmitterProcessor<Message<BookReturned>> bookReturnedEmitterProcessor() {
return EmitterProcessor.create();
}
}

View File

@@ -1,4 +1,5 @@
spring.cloud.stream.bindings.myinput.destination: inputDestination
spring.cloud.stream.bindings.myinput.contentType: application/json
spring.cloud.stream.bindings.output.destination: sentTo
spring.cloud.stream.bindings.output.contentType: application/json
spring.cloud.function.definition: bookReturned;bookSender;bookDeleted
spring.cloud.stream.bindings.bookSender-out-0.destination: inputDestination
spring.cloud.stream.bindings.bookReturned-in-0.destination: inputDestination
spring.cloud.stream.bindings.bookReturned-out-0.destination: bookReturned
spring.cloud.stream.bindings.bookDeleted-in-0.destination: bookDeleted

View File

@@ -24,12 +24,16 @@ import com.toomuchcoding.jsonassert.JsonAssertion
import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier
import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier
import org.springframework.cloud.contract.verifier.messaging.internal.ContractVerifierObjectMapper
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration
import org.springframework.context.annotation.Configuration
import org.springframework.messaging.Message
import org.springframework.test.context.ContextConfiguration
@@ -37,7 +41,7 @@ import org.springframework.test.context.ContextConfiguration
* SPIKE ON TESTS FROM NOTES IN MessagingSpec
*/
// Context configuration would end up in base class
@ContextConfiguration(classes = [StreamMessagingApplication], loader = SpringBootContextLoader)
@ContextConfiguration(classes = [Config, StreamMessagingApplication], loader = SpringBootContextLoader)
@SpringBootTest(properties = "debug=true")
@AutoConfigureMessageVerifier
class StreamMessagingApplicationSpec extends Specification {
@@ -55,7 +59,7 @@ class StreamMessagingApplicationSpec extends Specification {
triggeredBy('bookReturnedTriggered()')
}
outputMessage {
sentTo('output')
sentTo('bookReturned')
body('''{ "bookName" : "foo" }''')
headers {
header('BOOK-NAME', 'foo')
@@ -66,7 +70,7 @@ class StreamMessagingApplicationSpec extends Specification {
when:
bookReturnedTriggered()
then:
def response = contractVerifierMessaging.receive('output')
def response = contractVerifierMessaging.receive('bookReturned')
response.headers.get('BOOK-NAME') == 'foo'
and:
DocumentContext parsedJson = JsonPath.
@@ -79,7 +83,7 @@ class StreamMessagingApplicationSpec extends Specification {
def dsl = Contract.make {
label 'some_label'
input {
messageFrom('input')
messageFrom('inputDestination')
messageBody([
bookName: 'foo'
])
@@ -88,7 +92,7 @@ class StreamMessagingApplicationSpec extends Specification {
}
}
outputMessage {
sentTo('output')
sentTo('bookReturned')
body([
bookName: 'foo'
])
@@ -103,9 +107,9 @@ class StreamMessagingApplicationSpec extends Specification {
when:
contractVerifierMessaging.send(
contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']),
[sample: 'header'], 'input')
[sample: 'header'], 'inputDestination')
then:
def response = contractVerifierMessaging.receive('output')
def response = contractVerifierMessaging.receive('bookReturned')
response.headers.get('BOOK-NAME') == 'foo'
and:
DocumentContext parsedJson = JsonPath.
@@ -118,7 +122,7 @@ class StreamMessagingApplicationSpec extends Specification {
def dsl = Contract.make {
label 'some_label'
input {
messageFrom('delete')
messageFrom("bookDeleted")
messageBody([
bookName: 'foo'
])
@@ -134,7 +138,7 @@ class StreamMessagingApplicationSpec extends Specification {
when:
contractVerifierMessaging.
send(contractVerifierObjectMapper.writeValueAsString([bookName: 'foo']),
[sample: 'header'], 'delete')
[sample: 'header'], "bookDeleted")
then:
noExceptionThrown()
bookWasDeleted()
@@ -145,13 +149,20 @@ class StreamMessagingApplicationSpec extends Specification {
@Autowired
BookService bookService
@Autowired
BookListener bookListener
BookDeletedListener bookDeletedListener
void bookReturnedTriggered() {
bookService.returnBook(new BookReturned("foo"))
}
void bookWasDeleted() {
assert bookListener.bookSuccessfulyDeleted.get()
assert bookDeletedListener.bookSuccessfulyDeleted.get()
}
@Configuration
@EnableAutoConfiguration
@ImportAutoConfiguration(TestChannelBinderConfiguration)
static class Config {
}
}

View File

@@ -20,20 +20,20 @@ import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.stubrunner.StubFinder
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
import org.springframework.cloud.contract.stubrunner.spring.StubRunnerProperties
import org.springframework.cloud.stream.annotation.EnableBinding
import org.springframework.cloud.stream.annotation.StreamListener
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration
import org.springframework.cloud.stream.messaging.Sink
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.stereotype.Component
import org.springframework.test.context.ActiveProfiles
import org.springframework.test.context.ContextConfiguration
/**
* @author Marcin Grzejszczak
*/
@@ -75,7 +75,7 @@ class MocoHttpServerStubSpec extends Specification {
@Configuration
@EnableAutoConfiguration
@EnableBinding(Sink.class)
@ImportAutoConfiguration(TestChannelBinderConfiguration.class)
static class MocoConfig {
@Bean

View File

@@ -25,6 +25,7 @@ import spock.lang.Specification
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.autoconfigure.EnableAutoConfiguration
import org.springframework.boot.autoconfigure.ImportAutoConfiguration
import org.springframework.boot.test.context.SpringBootContextLoader
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.cloud.contract.spec.Contract
@@ -32,9 +33,7 @@ import org.springframework.cloud.contract.stubrunner.StubFinder
import org.springframework.cloud.contract.stubrunner.spring.AutoConfigureStubRunner
import org.springframework.cloud.contract.verifier.messaging.MessageVerifier
import org.springframework.cloud.contract.verifier.messaging.boot.AutoConfigureMessageVerifier
import org.springframework.cloud.stream.annotation.EnableBinding
import org.springframework.cloud.stream.messaging.Sink
import org.springframework.cloud.stream.messaging.Source
import org.springframework.cloud.stream.binder.test.TestChannelBinderConfiguration
import org.springframework.context.annotation.Configuration
import org.springframework.messaging.Message
import org.springframework.test.context.ContextConfiguration
@@ -221,7 +220,7 @@ class StreamStubRunnerSpec extends Specification {
// end::sample_dsl_3[]
@EnableBinding([Sink, Source])
@ImportAutoConfiguration(TestChannelBinderConfiguration.class)
@Configuration
@EnableAutoConfiguration
protected static class Config {}