4.4. 消费者存根生成
与 HTTP 部分不同,在消息传递中,我们需要在 JAR 中发布合约定义 一个存根。然后在消费者端解析它,并创建适当的存根路由。
如果类路径上有多个框架,则存根运行程序需要 定义应使用哪一个。假设你有AMQP、Spring Cloud Stream和Spring Integration。 在类路径上,并且您想要使用 Spring AMQP。然后你需要设置。 这样,唯一剩下的框架就是Spring AMQP。 |
4.4.1. 存根触发
要触发消息,请使用接口,如以下示例所示:StubTrigger
package org.springframework.cloud.contract.stubrunner;
import java.util.Collection;
import java.util.Map;
/**
* Contract for triggering stub messages.
*
* @author Marcin Grzejszczak
*/
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.
* @param ivyNotation ivy notation of a stub
* @param labelName name of the label to trigger
* @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.
* @param labelName name of the label to trigger
* @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();
/**
* Feature related to messaging.
* @return a mapping of ivy notation of a dependency to all the labels it has.
*/
Map<String, Collection<String>> labels();
}
为方便起见,界面扩展,因此您只需要一个 或测试中的其他。StubFinder
StubTrigger
StubTrigger
提供以下选项来触发消息:
- 按标签触发
- 按组和项目 ID 触发
- 由项目 ID 触发
- 触发所有消息
4.4.2. 标签触发
以下示例演示如何触发带有标签的消息:
stubFinder.trigger('return_book_1')
4.4.3. 按组和工件 ID 触发
以下示例演示如何按组和项目 ID 触发消息:
stubFinder.trigger('org.springframework.cloud.contract.verifier.stubs:streamService', 'return_book_1')
4.4.4. 由工件 ID 触发
以下示例演示如何从项目 ID 触发消息:
stubFinder.trigger('streamService', 'return_book_1')
4.4.5. 触发所有消息
以下示例演示如何触发所有消息:
stubFinder.trigger()
4.5. 使用 Apache Camel 的消费者端消息传递
Spring Cloud 合约存根运行器的消息传递模块为您提供了一种与 Apache Camel 集成的简单方法。 对于提供的工件,它会自动下载存根并注册所需的 路线。
4.5.1. 将 Apache Camel 添加到项目中
您可以在类路径上同时拥有 Apache Camel 和 Spring Cloud 合约存根运行程序。 记得用注释你的测试类。@AutoConfigureStubRunner
4.5.2. 禁用该功能
如果需要禁用此功能,请设置该属性。stubrunner.camel.enabled=false
4.5.3. 例子
假设我们有以下 Maven 存储库,其中包含为应用程序部署的存根:camelService
└── .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.xml
进一步假设存根包含以下结构:
├── META-INF
│ └── MANIFEST.MF
└── repository
├── accurest
│ ├── bookDeleted.groovy
│ ├── bookReturned1.groovy
│ └── bookReturned2.groovy
└── mappings
现在考虑以下合同(我们将其编号为 1 和 2):
Contract.make {
label 'return_book_1'
input {
triggeredBy('bookReturnedTriggered()')
}
outputMessage {
sentTo('jms:output')
body('''{ "bookName" : "foo" }''')
headers {
header('BOOK-NAME', 'foo')
}
}
}
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')
}
}
}
这些示例适用于三种方案:
- 方案 1(无输入消息)
- 场景 2(由输入触发的输出)
- 场景 3(无输出的输入)
方案 1(无输入消息)
要触发来自标签的消息,我们使用接口,如下所示:return_book_1
StubTrigger
stubFinder.trigger('return_book_1')
接下来,我们要侦听发送到以下消息的输出:jms:output
Exchange receivedMessage = consumerTemplate.receive('jms:output', 5000)
然后,收到的消息将传递以下断言:
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
场景 2(由输入触发的输出)
由于路由已为您设置,因此您可以向目的地发送消息。jms:output
producerTemplate.
sendBodyAndHeaders('jms:input', new BookReturned('foo'), [sample: 'header'])
接下来,我们要监听发送到的消息的输出,如下所示:jms:output
Exchange receivedMessage = consumerTemplate.receive('jms:output', 5000)
收到的消息将传递以下断言:
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.in.body)
receivedMessage.in.headers.get('BOOK-NAME') == 'foo'
场景 3(无输出的输入)
由于路由已为您设置,因此您可以向目的地发送消息,如下所示:jms:output
producerTemplate.
sendBodyAndHeaders('jms:delete', new BookReturned('foo'), [sample: 'header'])
4.6. 带有 Spring 集成的消费者端消息传递
Spring Cloud 合约存根运行器的消息传递模块为您提供了一种简单的方法 与弹簧集成集成。对于提供的工件,它会自动下载 存根并注册所需的路由。
4.6.1. 将运行器添加到项目中
您可以在 类路径。记得用注释你的测试类。@AutoConfigureStubRunner
4.6.2. 禁用该功能
如果需要禁用此功能,请设置该属性。stubrunner.integration.enabled=false
4.6.3. 例子
假设您有以下 Maven 存储库,其中包含为应用程序部署的存根:integrationService
└── .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.xml
进一步假设存根包含以下结构:
├── META-INF
│ └── MANIFEST.MF
└── repository
├── accurest
│ ├── bookDeleted.groovy
│ ├── bookReturned1.groovy
│ └── bookReturned2.groovy
└── mappings
考虑以下合同(编号为 1 和 2):
Contract.make {
label 'return_book_1'
input {
triggeredBy('bookReturnedTriggered()')
}
outputMessage {
sentTo('output')
body('''{ "bookName" : "foo" }''')
headers {
header('BOOK-NAME', 'foo')
}
}
}
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')
}
}
}
现在考虑以下 Spring 集成路线:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns="http://www.springframework.org/schema/integration"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration
http://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>
这些示例适用于三种方案:
- 方案 1(无输入消息)
- 场景 2(由输入触发的输出)
- 场景 3(无输出的输入)
方案 1(无输入消息)
要从标签触发消息,请使用接口,作为 遵循:return_book_1
StubTrigger
stubFinder.trigger('return_book_1')
下面的清单显示了如何侦听发送到的消息的输出:jms:output
Message<?> receivedMessage = messaging.receive('outputTest')
收到的消息将传递以下断言:
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
场景 2(由输入触发的输出)
由于路由已为您设置,因此您可以向目的地发送消息,如下所示:jms:output
messaging.send(new BookReturned('foo'), [sample: 'header'], 'input')
下面的清单显示了如何侦听发送到的消息的输出:jms:output
Message<?> receivedMessage = messaging.receive('outputTest')
收到的消息传递以下断言:
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
场景 3(无输出的输入)
由于路由已为您设置,因此您可以向目的地发送消息,如下所示:jms:input
messaging.send(new BookReturned('foo'), [sample: 'header'], 'delete')
4.7. 使用弹簧云流的消费者端消息传递
Spring Cloud 合约存根运行器的消息传递模块为您提供了一种简单的方法 与春溪融合。对于提供的工件,它会自动下载 存根并注册所需的路由。
如果存根运行程序与流字符串的集成 首先解析为通道,不存在这样的通道, 目标解析为通道名称。 |
如果要使用 Spring Cloud Stream,请记住添加依赖项测试支持,如下所示: 马文 格拉德尔 <dependency> |
4.7.1. 将运行器添加到项目中
您可以在 类路径。记得用注释你的测试类。@AutoConfigureStubRunner
4.7.2. 禁用该功能
如果需要禁用此功能,请设置该属性。stubrunner.stream.enabled=false
4.7.3. 例子
假设您有以下 Maven 存储库,其中包含为应用程序部署的存根:streamService
└── .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.xml
进一步假设存根包含以下结构:
├── META-INF
│ └── MANIFEST.MF
└── repository
├── accurest
│ ├── bookDeleted.groovy
│ ├── bookReturned1.groovy
│ └── bookReturned2.groovy
└── mappings
考虑以下合同(编号为 1 和 2):
Contract.make {
label 'return_book_1'
input { triggeredBy('bookReturnedTriggered()') }
outputMessage {
sentTo('returnBook')
body('''{ "bookName" : "foo" }''')
headers { header('BOOK-NAME', 'foo') }
}
}
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') }
}
}
现在考虑以下 Spring 配置:
stubrunner.repositoryRoot: classpath:m2repo/repository/
stubrunner.ids: org.springframework.cloud.contract.verifier.stubs:streamService:0.0.1-SNAPSHOT:stubs
stubrunner.stubs-mode: remote
spring:
cloud:
stream:
bindings:
output:
destination: returnBook
input:
destination: bookStorage
server:
port: 0
debug: true
这些示例适用于三种方案:
- 方案 1(无输入消息)
- 场景 2(由输入触发的输出)
- 场景 3(无输出的输入)
方案 1(无输入消息)
要从标签触发消息,请使用接口作为 遵循:return_book_1
StubTrigger
stubFinder.trigger('return_book_1')
以下示例演示如何侦听发送到通道 whoseis 的消息输出:destination
returnBook
Message<?> receivedMessage = messaging.receive('returnBook')
收到的消息传递以下断言:
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
场景 2(由输入触发的输出)
由于路由已为您设置,因此您可以向 发送消息,如下所示:bookStorage
destination
messaging.send(new BookReturned('foo'), [sample: 'header'], 'bookStorage')
以下示例演示如何侦听发送到的消息的输出:returnBook
Message<?> receivedMessage = messaging.receive('returnBook')
收到的消息传递以下断言:
receivedMessage != null
assertJsons(receivedMessage.payload)
receivedMessage.headers.get('BOOK-NAME') == 'foo'
场景 3(无输出的输入)
由于路由已为您设置,因此您可以向目的地发送消息,如下所示:jms:output
messaging.send(new BookReturned('foo'), [sample: 'header'], 'delete')
4.8. 使用 Spring AMQP 的消费者端消息传递
Spring Cloud 合约存根运行器的消息传递模块提供了一种简单的方法 与Spring AMQP的兔子模板集成。对于提供的工件,它 自动下载存根并注册所需的路由。
集成尝试独立工作(即,不与正在运行的交互 RabbitMQ 消息代理)。它期望应用程序上下文和 将其用作名为的 Spring 启动测试。因此,它可以使用Mockito间谍 用于验证和检查应用程序发送的消息的功能。RabbitTemplate
@SpyBean
在消息使用者端,存根运行器认为是全注释的 应用程序上下文中的终结点和所有对象。@RabbitListener
SimpleMessageListenerContainer
由于消息通常发送到AMQP中的交易所,因此消息合约包含 交换名称作为目标。另一端的消息侦听器绑定到 队列。绑定将交换连接到队列。如果触发了消息协定,则 Spring AMQP 存根运行器集成在应用程序上下文中查找绑定 匹配此交换。然后它从 Spring 交易所收集队列并尝试 查找绑定到这些队列的消息侦听器。为所有匹配触发消息 消息侦听器。
如果需要使用路由密钥,可以使用消息标头传递它们。amqp_receivedRoutingKey
4.8.1. 将运行器添加到项目中
您可以在类路径上同时拥有 Spring AMQP 和 Spring Cloud 合约存根运行程序,并且 设置属性。记得为您的测试类添加注释 跟。stubrunner.amqp.enabled=true
@AutoConfigureStubRunner
如果类路径上已有流和集成,则需要 通过设置属性来显式禁用它们。 |
4.8.2. 例子
假设您有以下 Maven 存储库,其中包含为应用程序部署的存根:spring-cloud-contract-amqp-test
└── .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.xml
进一步假设存根包含以下结构:
├── META-INF
│ └── MANIFEST.MF
└── contracts
└── shouldProduceValidPersonData.groovy
然后考虑以下合同:
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"
])
}
}
现在考虑以下 Spring 配置:
stubrunner:
repositoryRoot: classpath:m2repo/repository/
ids: org.springframework.cloud.contract.verifier.stubs.amqp:spring-cloud-contract-amqp-test:0.4.0-SNAPSHOT:stubs
stubs-mode: remote
amqp:
enabled: true
server:
port: 0
触发消息
要使用上一节中的协定触发消息,请使用接口,如 遵循:StubTrigger
stubTrigger.trigger("contract-test.person.created.event")
消息的目的地为 ,因此 Spring AMQP 存根运行程序 集成查找与此交换相关的绑定,如以下示例所示:contract-test.exchange
@Bean
public Binding binding() {
return BindingBuilder.bind(new Queue("test.queue")).to(new DirectExchange("contract-test.exchange"))
.with("#");
}
绑定定义绑定调用的队列。结果,以下侦听器 定义与合约消息匹配和调用:test.queue
@Bean
public SimpleMessageListenerContainer simpleMessageListenerContainer(ConnectionFactory connectionFactory,
MessageListenerAdapter listenerAdapter) {
SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.setQueueNames("test.queue");
container.setMessageListener(listenerAdapter);
return container;
}
此外,以下带批注的侦听器匹配并被调用:
@RabbitListener(bindings = @QueueBinding(value = @Queue("test.queue"),
exchange = @Exchange(value = "contract-test.exchange", ignoreDeclarationExceptions = "true")))
public void handlePerson(Person person) {
this.person = person;
}
消息直接交给与匹配相关联的方法。 |
春季 AMQP 测试配置
为了避免Spring AMQP在我们的测试期间试图连接到正在运行的代理,我们 配置模拟。ConnectionFactory
要禁用模拟,请设置以下属性:,如下所示:ConnectionFactory
stubrunner.amqp.mockConnection=false
stubrunner:
amqp:
mockConnection: false
4.9. 使用 Spring JMS 的消费者端消息传递
Spring Cloud 合约存根运行器的消息传递模块提供了一种简单的方法 与Spring JMS集成。
集成假定您有一个正在运行的 JMS 代理实例(例如嵌入式代理)。activemq
4.9.1. 将运行器添加到项目中
你需要在类路径上同时拥有 Spring JMS 和 Spring Cloud 合约存根运行程序。记得为您的测试类添加注释 跟。@AutoConfigureStubRunner
4.9.2. 例子
假设存根结构如下所示:
├── stubs
├── bookDeleted.groovy
├── bookReturned1.groovy
└── bookReturned2.groovy
进一步假设以下测试配置:
stubrunner:
repository-root: stubs:classpath:/stubs/
ids: my:stubs
stubs-mode: remote
spring:
activemq:
send-timeout: 1000
jms:
template:
receive-timeout: 1000
现在考虑以下合同(我们将其编号为 1 和 2):
Contract.make {
label 'return_book_1'
input {
triggeredBy('bookReturnedTriggered()')
}
outputMessage {
sentTo('output')
body('''{ "bookName" : "foo" }''')
headers {
header('BOOK-NAME', 'foo')
}
}
}
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')
}
}
}
方案 1(无输入消息)
要触发来自标签的消息,我们使用接口,如下所示:return_book_1
StubTrigger
stubFinder.trigger('return_book_1')
接下来,我们要侦听发送到以下消息的输出:output
TextMessage receivedMessage = (TextMessage) jmsTemplate.receive('output')
然后,收到的消息将传递以下断言:
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.getText())
receivedMessage.getStringProperty('BOOK-NAME') == 'foo'
场景 2(由输入触发的输出)
由于路由已为您设置,因此您可以向目的地发送消息。output
jmsTemplate.
convertAndSend('input', new BookReturned('foo'), new MessagePostProcessor() {
@Override
Message postProcessMessage(Message message) throws JMSException {
message.setStringProperty("sample", "header")
return message
}
})
接下来,我们要监听发送到的消息的输出,如下所示:output
TextMessage receivedMessage = (TextMessage) jmsTemplate.receive('output')
收到的消息将传递以下断言:
receivedMessage != null
assertThatBodyContainsBookNameFoo(receivedMessage.getText())
receivedMessage.getStringProperty('BOOK-NAME') == 'foo'
场景 3(无输出的输入)
由于路由已为您设置,因此您可以向目的地发送消息,如下所示:output
jmsTemplate.
convertAndSend('delete', new BookReturned('foo'), new MessagePostProcessor() {
@Override
Message postProcessMessage(Message message) throws JMSException {
message.setStringProperty("sample", "header")
return message
}
})
4.10. 使用 Spring Kafka 的消费者端消息传递
Spring Cloud 合约存根运行器的消息传递模块提供了一种简单的方法 与春天的卡夫卡集成。
集成假定您有一个嵌入式 Kafka 代理的运行实例(通过依赖关系)。spring-kafka-test
4.10.1. 将运行器添加到项目中
你需要在类路径上有 Spring Kafka、Spring Kafka 测试(运行)和 Spring Cloud 合约存根运行器。记得为您的测试类添加注释 跟。@EmbeddedBroker
@AutoConfigureStubRunner
通过 Kafka 集成,为了轮询单个消息,我们需要在 Spring 上下文启动时注册一个消费者。这可能会导致这样一种情况:当您在使用者端时,存根运行程序可以为同一组 ID 和主题注册额外的使用者。这可能会导致只有一个组件实际轮询消息的情况。由于在使用者端,您同时拥有 Spring Cloud 合约存根运行程序和 Spring Cloud 合约验证程序类路径,因此我们需要能够关闭此类行为。这是通过 theflag 自动完成的,这会禁用联系人验证程序使用者注册。如果应用程序既是 Kafka 消息的使用者又是生成者,则可能需要手动将该属性切换为生成的测试的基类。stubrunner.kafka.initializer.enabled
false
如果您有多个 bean,则可以提供自己的 bean 类型,以返回您选择的 bean。KafkaTemplate
Supplier<KafkaTemplate>
KafkaTemplate
4.10.2. 例子
假设存根结构如下所示:
├── stubs
├── bookDeleted.groovy
├── bookReturned1.groovy
└── bookReturned2.groovy
进一步假设以下测试配置(注意指向嵌入式代理的 IP 通过):spring.kafka.bootstrap-servers
${spring.embedded.kafka.brokers}
stubrunner:
repository-root: stubs:classpath:/stubs/
ids: my:stubs
stubs-mode: remote
spring:
kafka:
bootstrap-servers: ${spring.embedded.kafka.brokers}
producer:
value-serializer: org.springframework.kafka.support.serializer.JsonSerializer
properties:
"spring.json.trusted.packages": "*"
consumer:
value-deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
properties:
"spring.json.trusted.packages": "*"
group-id: groupId
如果您的应用程序使用非整数记录键,则需要相应地设置 and 属性,因为 Kafka 反序列化需要非空 记录键为整数类型。 |
现在考虑以下合同(我们将其编号为 1 和 2):
Contract.make {
label 'return_book_1'
input {
triggeredBy('bookReturnedTriggered()')
}
outputMessage {
sentTo('output')
body('''{ "bookName" : "foo" }''')
headers {
header('BOOK-NAME', 'foo')
}
}
}
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')
}
}
}
方案 1(无输入消息)
要触发来自标签的消息,我们使用接口,如下所示:return_book_1
StubTrigger
stubFinder.trigger('return_book_1')
接下来,我们要侦听发送到以下消息的输出:output
Message receivedMessage = receiveFromOutput()
然后,收到的消息将传递以下断言:
assert receivedMessage != null
assert assertThatBodyContainsBookNameFoo(receivedMessage.getPayload())
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'foo'
场景 2(由输入触发的输出)
由于路由已为您设置,因此您可以向目的地发送消息。output
Message message = MessageBuilder.createMessage(new BookReturned('foo'), new MessageHeaders([sample: "header",]))
kafkaTemplate.setDefaultTopic('input')
kafkaTemplate.send(message)
Message message = MessageBuilder.createMessage(new BookReturned('bar'), new MessageHeaders([kafka_messageKey: "bar5150",]))
kafkaTemplate.setDefaultTopic('input2')
kafkaTemplate.send(message)
接下来,我们要监听发送到的消息的输出,如下所示:output
Message receivedMessage = receiveFromOutput()
Message receivedMessage = receiveFromOutput()
收到的消息将传递以下断言:
assert receivedMessage != null
assert assertThatBodyContainsBookNameFoo(receivedMessage.getPayload())
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'foo'
assert receivedMessage != null
assert assertThatBodyContainsBookName(receivedMessage.getPayload(), 'bar')
assert receivedMessage.getHeaders().get('BOOK-NAME') == 'bar'
assert receivedMessage.getHeaders().get("kafka_receivedMessageKey") == 'bar5150'
场景 3(无输出的输入)
由于路由已为您设置,因此您可以向目的地发送消息,如下所示:output
Message message = MessageBuilder.createMessage(new BookReturned('foo'), new MessageHeaders([sample: "header",]))
kafkaTemplate.setDefaultTopic('delete')
kafkaTemplate.send(message)
5. 春云合约存根跑者
使用Spring Cloud合约验证程序时可能遇到的问题之一是 将生成的 WireMock JSON 存根从服务器端传递到客户端(或 各种客户)。在消息传递的客户端生成方面也是如此。
复制 JSON 文件并手动设置消息传递的客户端不在 问题。这就是我们引入春云合约存根运行器的原因。它可以 自动下载并运行存根。
5.1. 快照版本
您可以将其他快照存储库添加到构建文件中以使用快照 版本,每次成功构建后都会自动上传,如下所示:
<repositories>
<repository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
<pluginRepositories>
<pluginRepository>
<id>spring-snapshots</id>
<name>Spring Snapshots</name>
<url>https://repo.spring.io/snapshot</url>
<snapshots>
<enabled>true</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
<pluginRepository>
<id>spring-releases</id>
<name>Spring Releases</name>
<url>https://repo.spring.io/release</url>
<snapshots>
<enabled>false</enabled>
</snapshots>
</pluginRepository>
</pluginRepositories>
5.2. 将存根发布为 JAR
将存根发布为 jar 的最简单方法是集中存根的保存方式。 例如,您可以将它们作为 jar 保存在 Maven 存储库中。
对于Maven和Gradle来说,设置已经准备好工作了。但是,您可以自定义 如果你愿意的话。 |
以下示例演示如何将存根发布为 jar:
<!-- First disable the default jar setup in the properties section -->
<!-- we don't want the verifier to do a jar for us -->
<spring.cloud.contract.verifier.skip>true</spring.cloud.contract.verifier.skip>
<!-- Next add the assembly plugin to your build -->
<!-- we want the assembly plugin to generate the JAR -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
<executions>
<execution>
<id>stub</id>
<phase>prepare-package</phase>
<goals>
<goal>single</goal>
</goals>
<inherited>false</inherited>
<configuration>
<attach>true</attach>
<descriptors>
$/tmp/releaser-1667490065708-0/spring-cloud-contract/docs/src/assembly/stub.xml
</descriptors>
</configuration>
</execution>
</executions>
</plugin>
<!-- Finally setup your assembly. Below you can find the contents of src/main/assembly/stub.xml -->
<assembly
xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.3 https://maven.apache.org/xsd/assembly-1.1.3.xsd">
<id>stubs</id>
<formats>
<format>jar</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<directory>src/main/java</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>**com/example/model/*.*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/classes</directory>
<outputDirectory>/</outputDirectory>
<includes>
<include>**com/example/model/*.*</include>
</includes>
</fileSet>
<fileSet>
<directory>${project.build.directory}/snippets/stubs</directory>
<outputDirectory>META-INF/${project.groupId}/${project.artifactId}/${project.version}/mappings</outputDirectory>
<includes>
<include>**/*</include>
</includes>
</fileSet>
<fileSet>
<directory>$/tmp/releaser-1667490065708-0/spring-cloud-contract/docs/src/test/resources/contracts</directory>
<outputDirectory>META-INF/${project.groupId}/${project.artifactId}/${project.version}/contracts</outputDirectory>
<includes>
<include>**/*.groovy</include>
</includes>
</fileSet>
</fileSets>
</assembly>
5.3. 短截流道核心
存根运行器核心为服务协作者运行存根。将存根视为 服务允许您使用存根运行程序作为消费者驱动合约的实现。
存根运行程序允许您自动下载提供的依赖项的存根(或 从类路径中选择那些),为它们启动 WireMock 服务器,并为它们提供适当的 存根定义。对于消息传递,定义了特殊的存根路由。
5.3.1. 检索存根
您可以从以下获取存根的选项中进行选择:
- 基于以太的解决方案,可从Artifactory或Nexus下载带有存根的JAR。
- 类路径扫描解决方案,它使用模式搜索类路径以检索存根
- 编写自己的实现以进行完全自定义
org.springframework.cloud.contract.stubrunner.StubDownloaderBuilder
后一个示例在自定义存根运行程序部分中介绍。
下载存根
您可以使用开关控制存根的下载。它从枚举中选取值。您可以使用以下选项:stubsMode
StubRunnerProperties.StubsMode
-
StubRunnerProperties.StubsMode.CLASSPATH
(默认值):从类路径中选取存根 -
StubRunnerProperties.StubsMode.LOCAL
:从本地存储中选取存根(例如,.m2
) -
StubRunnerProperties.StubsMode.REMOTE
:从远程位置选取存根
以下示例从本地位置选取存根:
@AutoConfigureStubRunner(repositoryRoot="https://foo.bar", ids = "com.example:beer-api-producer:+:stubs:8095", stubsMode = StubRunnerProperties.StubsMode.LOCAL)
类路径扫描
如果将属性设置为(或不设置默认值),则会扫描类路径。 请考虑以下示例:stubsMode
StubRunnerProperties.StubsMode.CLASSPATH
CLASSPATH
@AutoConfigureStubRunner(ids = {
"com.example:beer-api-producer:+:stubs:8095",
"com.example.foo:bar:1.0.0:superstubs:8096"
})
可以将依赖项添加到类路径中,如下所示:
<dependency>
<groupId>com.example</groupId>
<artifactId>beer-api-producer-restdocs</artifactId>
<classifier>stubs</classifier>
<version>0.0.1-SNAPSHOT</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>com.example.thing1</groupId>
<artifactId>thing2</artifactId>
<classifier>superstubs</classifier>
<version>1.0.0</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
然后扫描类路径上的指定位置。为 扫描以下位置:com.example:beer-api-producer-restdocs
- /META-INF/com.example/beer-api-producer-restdocs/*/.*
- /contracts/com.example/beer-api-producer-restdocs/*/.*
- /mappings/com.example/beer-api-producer-restdocs/*/.*
为此,将扫描以下位置:com.example.thing1:thing2
- /META-INF/com.example.thing1/thing2/*/.*
- /contracts/com.example.thing1/thing2/*/.*
- /mappings/com.example.thing1/thing2/*/.*
打包 生产者存根。 |
为了实现适当的存根包装,生产商将按如下方式设置合同:
└── src
└── test
└── resources
└── contracts
└── com.example
└── beer-api-producer-restdocs
└── nested
└── contract3.groovy
通过使用Maven程序集插件或Gradle Jar任务,您必须创建以下内容 存根罐中的结构:
└── META-INF
└── com.example
└── beer-api-producer-restdocs
└── 2.0.0
├── contracts
│ └── nested
│ └── contract2.groovy
└── mappings
└── mapping.json
通过维护此结构,可以扫描类路径,您可以从消息传递或 无需下载工件的 HTTP 存根。
配置 HTTP 服务器存根
存根运行器有一个抽象底层的概念 HTTP 服务器的具体实现(例如,WireMock 是实现之一)。 有时,您需要对存根服务器执行一些额外的调整(这对于给定的实现是具体的)。 为此,存根运行器为您提供 批注中可用的属性和 JUnit 规则,可通过系统属性访问,您可以在其中提供 您的接口实现。实现可以改变 给定 HTTP 服务器存根的配置文件。HttpServerStub
httpServerStubConfigurer
org.springframework.cloud.contract.stubrunner.HttpServerStubConfigurer
Spring Cloud 合约存根运行程序附带一个实现,您 可以扩展为 WireMock:。 在方法中, 您可以为给定存根提供自己的自定义配置。用途 案例可能是在HTTPS端口上为给定的项目ID启动WireMock。以下 示例显示了如何执行此操作:org.springframework.cloud.contract.stubrunner.provider.wiremock.WireMockHttpServerStubConfigurer
configure
例 1.WireMockHttpServerStubConfigurer 实现
@CompileStatic
static class HttpsForFraudDetection extends WireMockHttpServerStubConfigurer {
private static final Log log = LogFactory.getLog(HttpsForFraudDetection)
@Override
WireMockConfiguration configure(WireMockConfiguration httpStubConfiguration, HttpServerStubConfiguration httpServerStubConfiguration) {
if (httpServerStubConfiguration.stubConfiguration.artifactId == "fraudDetectionServer") {
int httpsPort = SocketUtils.findAvailableTcpPort()
log.info("Will set HTTPs port [" + httpsPort + "] for fraud detection server")
return httpStubConfiguration
.httpsPort(httpsPort)
}
return httpStubConfiguration
}
}
然后,您可以将其与注释重用,如下所示:@AutoConfigureStubRunner
@AutoConfigureStubRunner(mappingsOutputFolder = "target/outputmappings/",
httpServerStubConfigurer = HttpsForFraudDetection)
每当找到 HTTPS 端口时,它都会优先于 HTTP 端口。
5.3.2. 运行存根
本节介绍如何运行存根。它包含以下主题:
- HTTP 存根
- 查看已注册的映射
- 消息传递存根
HTTP 存根
存根在 JSON 文档中定义,其语法在WireMock 文档中定义。
以下示例在 JSON 中定义存根:
{
"request": {
"method": "GET",
"url": "/ping"
},
"response": {
"status": 200,
"body": "pong",
"headers": {
"Content-Type": "text/plain"
}
}
}
查看已注册的映射
每个存根协作者都会在端点下公开一个已定义映射的列表。__/admin/
还可以使用该属性将映射转储到文件。 对于基于注释的方法,它将类似于以下示例:mappingsOutputFolder
@AutoConfigureStubRunner(ids="a.b.c:loanIssuance,a.b.c:fraudDetectionServer",
mappingsOutputFolder = "target/outputmappings/")
对于 JUnit 方法,它类似于以下示例:
@ClassRule @Shared StubRunnerRule rule = new StubRunnerRule()
.repoRoot("https://some_url")
.downloadStub("a.b.c", "loanIssuance")
.downloadStub("a.b.c:fraudDetectionServer")
.withMappingsOutputFolder("target/outputmappings")
然后,如果您签出文件夹,您将看到以下结构;target/outputmappings
.
├── fraudDetectionServer_13705
└── loanIssuance_12255
这意味着有两个存根注册。如果我们看一下其中一个文件,我们会看到(对于WireMock) 可用于给定服务器的映射:fraudDetectionServer
13705
loanIssuance
12255
[{
"id" : "f9152eb9-bf77-4c38-8289-90be7d10d0d7",
"request" : {
"url" : "/name",
"method" : "GET"
},
"response" : {
"status" : 200,
"body" : "fraudDetectionServer"
},
"uuid" : "f9152eb9-bf77-4c38-8289-90be7d10d0d7"
},
...
]
消息传递存根
根据提供的存根运行程序依赖项和 DSL,将自动设置消息传递路由。
5.4. 存根运行器 JUnit 规则和存根运行器 JUnit5 扩展
存根运行器附带一个 JUnit 规则,可让您下载和运行给定的存根 组和项目 ID,如以下示例所示:
@ClassRule
public static StubRunnerRule rule = new StubRunnerRule().repoRoot(repoRoot())
.stubsMode(StubRunnerProperties.StubsMode.REMOTE)
.downloadStub("org.springframework.cloud.contract.verifier.stubs", "loanIssuance")
.downloadStub("org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer");
@BeforeClass
@AfterClass
public static void setupProps() {
System.clearProperty("stubrunner.repository.root");
System.clearProperty("stubrunner.classifier");
}
Ais 也可用于 JUnit 5.和工作,以非常相似的方式。在规则或扩展名之后 调用,存根运行器连接到您的 Maven 存储库,对于给定的列表 依赖项,尝试:StubRunnerExtension
StubRunnerRule
StubRunnerExtension
- 下载它们
- 在本地缓存它们
- 将它们解压缩到临时文件夹
- 在提供的随机端口上为每个Maven依赖项启动WireMock服务器 端口范围或提供的端口
- 向 WireMock 服务器提供所有有效的 WireMock 定义的 JSON 文件
- 发送消息(记得传递接口的实现)
MessageVerifier
Stub Runner 使用Eclipse Aether机制来下载 Maven 依赖项。 查看他们的文档以获取更多信息。
自从实施以来,他们让 您将找到已启动的存根,如以下示例所示:StubRunnerRule
StubRunnerExtension
StubFinder
package org.springframework.cloud.contract.stubrunner;
import java.net.URL;
import java.util.Collection;
import java.util.Map;
import org.springframework.cloud.contract.spec.Contract;
/**
* Contract for finding registered stubs.
*
* @author Marcin Grzejszczak
*/
public interface StubFinder extends StubTrigger {
/**
* For the given groupId and artifactId tries to find the matching URL of the running
* stub.
* @param groupId - might be null. In that case a search only via artifactId takes
* place
* @param artifactId - artifact id of the stub
* @return URL of a running stub or throws exception if not found
* @throws StubNotFoundException in case of not finding a stub
*/
URL findStubUrl(String groupId, String artifactId) throws StubNotFoundException;
/**
* For the given Ivy notation {@code [groupId]:artifactId:[version]:[classifier]}
* tries to find the matching URL of the running stub. You can also pass only
* {@code artifactId}.
* @param ivyNotation - Ivy representation of the Maven artifact
* @return URL of a running stub or throws exception if not found
* @throws StubNotFoundException in case of not finding a stub
*/
URL findStubUrl(String ivyNotation) throws StubNotFoundException;
/**
* @return all running stubs
*/
RunningStubs findAllRunningStubs();
/**
* @return the list of Contracts
*/
Map<StubConfiguration, Collection<Contract>> getContracts();
}
以下示例提供了有关使用存根运行程序的更多详细信息:
@ClassRule
@Shared
StubRunnerRule rule = new StubRunnerRule()
.stubsMode(StubRunnerProperties.StubsMode.REMOTE)
.repoRoot(StubRunnerRuleSpec.getResource("/m2repo/repository").toURI().toString())
.downloadStub("org.springframework.cloud.contract.verifier.stubs", "loanIssuance")
.downloadStub("org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer")
.withMappingsOutputFolder("target/outputmappingsforrule")
def 'should start WireMock servers'() {
expect: 'WireMocks are running'
rule.findStubUrl('org.springframework.cloud.contract.verifier.stubs', 'loanIssuance') != null
rule.findStubUrl('loanIssuance') != null
rule.findStubUrl('loanIssuance') == rule.findStubUrl('org.springframework.cloud.contract.verifier.stubs', 'loanIssuance')
rule.findStubUrl('org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer') != null
and:
rule.findAllRunningStubs().isPresent('loanIssuance')
rule.findAllRunningStubs().isPresent('org.springframework.cloud.contract.verifier.stubs', 'fraudDetectionServer')
rule.findAllRunningStubs().isPresent('org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer')
and: 'Stubs were registered'
"${rule.findStubUrl('loanIssuance').toString()}/name".toURL().text == 'loanIssuance'
"${rule.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer'
}
def 'should output mappings to output folder'() {
when:
def url = rule.findStubUrl('fraudDetectionServer')
then:
new File("target/outputmappingsforrule", "fraudDetectionServer_${url.port}").exists()
}
请参阅JUnit 和 Spring 的通用属性以获取有关以下内容的更多信息 如何应用存根运行程序的全局配置。
要将 JUnit 规则或 JUnit 5 扩展与消息传递一起使用,您必须向规则生成器提供接口的实现(例如,)。 如果不这样做,则每当您尝试发送消息时,都会引发异常。 |
5.4.1. Maven 设置
存根下载器遵循不同本地存储库文件夹的 Maven 设置。 存储库和配置文件的身份验证详细信息目前未考虑在内, 因此,您需要使用上述属性来指定它。
5.4.2. 提供固定端口
您还可以在固定端口上运行存根。您可以通过两种不同的方式执行此操作。 一种是在属性中传递它,另一种是使用 JUnit 规则。
5.4.3. 流畅的接口
使用理论时,可以添加存根下载 然后传递上次下载的存根的端口。以下示例演示如何执行此操作:StubRunnerRule
StubRunnerExtension
@ClassRule
public static StubRunnerRule rule = new StubRunnerRule().repoRoot(repoRoot())
.stubsMode(StubRunnerProperties.StubsMode.REMOTE)
.downloadStub("org.springframework.cloud.contract.verifier.stubs", "loanIssuance").withPort(12345)
.downloadStub("org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer:12346");
@BeforeClass
@AfterClass
public static void setupProps() {
System.clearProperty("stubrunner.repository.root");
System.clearProperty("stubrunner.classifier");
}
对于前面的示例,以下测试有效:
then(rule.findStubUrl("loanIssuance")).isEqualTo(URI.create("http://localhost:12345").toURL());
then(rule.findStubUrl("fraudDetectionServer")).isEqualTo(URI.create("http://localhost:12346").toURL());
5.4.4. 带弹簧的短截流道
带弹簧的存根运行器设置了存根运行程序项目的弹簧配置。
通过在配置文件中提供存根列表,存根运行器会自动下载 并在 WireMock 中注册选定的存根。
如果要查找存根依赖项的 URL,可以自动连线接口并使用 其方法如下:StubFinder
@SpringBootTest(classes = Config, properties = [" stubrunner.cloud.enabled=false",
'foo=${stubrunner.runningstubs.fraudDetectionServer.port}',
'fooWithGroup=${stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port}'])
@AutoConfigureStubRunner(mappingsOutputFolder = "target/outputmappings/",
httpServerStubConfigurer = HttpsForFraudDetection)
@ActiveProfiles("test")
class StubRunnerConfigurationSpec extends Specification {
@Autowired
StubFinder stubFinder
@Autowired
Environment environment
@StubRunnerPort("fraudDetectionServer")
int fraudDetectionServerPort
@StubRunnerPort("org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer")
int fraudDetectionServerPortWithGroupId
@Value('${foo}')
Integer foo
void setupSpec() {
System.clearProperty("stubrunner.repository.root")
System.clearProperty("stubrunner.classifier")
WireMockHttpServerStubAccessor.clear()
}
void cleanupSpec() {
setupSpec()
}
def 'should mark all ports as random'() {
expect:
WireMockHttpServerStubAccessor.everyPortRandom()
}
def 'should start WireMock servers'() {
expect: 'WireMocks are running'
stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs', 'loanIssuance') != null
stubFinder.findStubUrl('loanIssuance') != null
stubFinder.findStubUrl('loanIssuance') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs', 'loanIssuance')
stubFinder.findStubUrl('loanIssuance') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:loanIssuance')
stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT') == stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:loanIssuance:0.0.1-SNAPSHOT:stubs')
stubFinder.findStubUrl('org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer') != null
and:
stubFinder.findAllRunningStubs().isPresent('loanIssuance')
stubFinder.findAllRunningStubs().isPresent('org.springframework.cloud.contract.verifier.stubs', 'fraudDetectionServer')
stubFinder.findAllRunningStubs().isPresent('org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer')
and: 'Stubs were registered'
"${stubFinder.findStubUrl('loanIssuance').toString()}/name".toURL().text == 'loanIssuance'
"${stubFinder.findStubUrl('fraudDetectionServer').toString()}/name".toURL().text == 'fraudDetectionServer'
and: 'Fraud Detection is an HTTPS endpoint'
stubFinder.findStubUrl('fraudDetectionServer').toString().startsWith("https")
}
def 'should throw an exception when stub is not found'() {
when:
stubFinder.findStubUrl('nonExistingService')
then:
thrown(StubNotFoundException)
when:
stubFinder.findStubUrl('nonExistingGroupId', 'nonExistingArtifactId')
then:
thrown(StubNotFoundException)
}
def 'should register started servers as environment variables'() {
expect:
environment.getProperty("stubrunner.runningstubs.loanIssuance.port") != null
stubFinder.findAllRunningStubs().getPort("loanIssuance") == (environment.getProperty("stubrunner.runningstubs.loanIssuance.port") as Integer)
and:
environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") != null
stubFinder.findAllRunningStubs().getPort("fraudDetectionServer") == (environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") as Integer)
and:
environment.getProperty("stubrunner.runningstubs.fraudDetectionServer.port") != null
stubFinder.findAllRunningStubs().getPort("fraudDetectionServer") == (environment.getProperty("stubrunner.runningstubs.org.springframework.cloud.contract.verifier.stubs.fraudDetectionServer.port") as Integer)
}
def 'should be able to interpolate a running stub in the passed test property'() {
given:
int fraudPort = stubFinder.findAllRunningStubs().getPort("fraudDetectionServer")
expect:
fraudPort > 0
environment.getProperty("foo", Integer) == fraudPort
environment.getProperty("fooWithGroup", Integer) == fraudPort
foo == fraudPort
}
@Issue("#573")
def 'should be able to retrieve the port of a running stub via an annotation'() {
given:
int fraudPort = stubFinder.findAllRunningStubs().getPort("fraudDetectionServer")
expect:
fraudPort > 0
fraudDetectionServerPort == fraudPort
fraudDetectionServerPortWithGroupId == fraudPort
}
def 'should dump all mappings to a file'() {
when:
def url = stubFinder.findStubUrl("fraudDetectionServer")
then:
new File("target/outputmappings/", "fraudDetectionServer_${url.port}").exists()
}
@Configuration
@EnableAutoConfiguration
static class Config {}
@CompileStatic
static class HttpsForFraudDetection extends WireMockHttpServerStubConfigurer {
private static final Log log = LogFactory.getLog(HttpsForFraudDetection)
@Override
WireMockConfiguration configure(WireMockConfiguration httpStubConfiguration, HttpServerStubConfiguration httpServerStubConfiguration) {
if (httpServerStubConfiguration.stubConfiguration.artifactId == "fraudDetectionServer") {
int httpsPort = SocketUtils.findAvailableTcpPort()
log.info("Will set HTTPs port [" + httpsPort + "] for fraud detection server")
return httpStubConfiguration
.httpsPort(httpsPort)
}
return httpStubConfiguration
}
}
}
这样做取决于以下配置文件:
stubrunner:
repositoryRoot: classpath:m2repo/repository/
ids:
- org.springframework.cloud.contract.verifier.stubs:loanIssuance
- org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer
- org.springframework.cloud.contract.verifier.stubs:bootService
stubs-mode: remote
除了使用属性,您还可以使用其中的属性。 下面的示例通过在批注上设置值来实现相同的结果:@AutoConfigureStubRunner
@AutoConfigureStubRunner(
ids = ["org.springframework.cloud.contract.verifier.stubs:loanIssuance",
"org.springframework.cloud.contract.verifier.stubs:fraudDetectionServer",
"org.springframework.cloud.contract.verifier.stubs:bootService"],
stubsMode = StubRunnerProperties.StubsMode.REMOTE,
repositoryRoot = "classpath:m2repo/repository/")
存根流道弹簧按以下方式注册环境变量 对于每个已注册的 WireMock 服务器。以下示例显示了以下对象的存根运行程序 ID:com.example:thing1
com.example:thing2
-
stubrunner.runningstubs.thing1.port
-
stubrunner.runningstubs.com.example.thing1.port
-
stubrunner.runningstubs.thing2.port
-
stubrunner.runningstubs.com.example.thing2.port
可以在代码中引用这些值。
您还可以使用注释来注入正在运行的存根的端口。 注释的值只能是理论或。 以下示例工作显示了存根运行程序 ID。@StubRunnerPort
groupid:artifactid
artifactid
com.example:thing1
com.example:thing2
@StubRunnerPort("thing1")标签:Spring,stubs,contract,存根,cloud,org,foo,合约,Cloud From: https://blog.51cto.com/u_15326439/5900009
int thing1Port;
@StubRunnerPort("com.example:thing2")
int thing2Port;