Een serviceverbinding is een verbinding met elke externe service. De automatische configuratie van Spring Boot kan de details van een serviceverbinding verbruiken en deze gebruiken om een verbinding met een externe service tot stand te brengen. Als u dit doet, hebben de verbindingsgegevens voorrang op eventuele verbindingsgerelateerde configuratie-eigenschappen.
Wanneer u Docker Compose gebruikt, kunt u automatisch verbindingsgegevens maken voor een service die in een container wordt uitgevoerd door de @SpringBootTest aantekening toe te voegen met de spring.docker.compose.file eigenschap in de testklasse.
De volgende tabel bevat informatie over de factoryklassen voor verbindingsgegevens die worden ondersteund in de spring-cloud-azure-docker-compose JAR:
<properties>
<version.spring.cloud.azure>7.1.0</version.spring.cloud.azure>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-dependencies</artifactId>
<version>${version.spring.cloud.azure}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-storage-blob</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-docker-compose</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<version.spring.cloud.azure>7.1.0</version.spring.cloud.azure>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-dependencies</artifactId>
<version>${version.spring.cloud.azure}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-storage-queue</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-docker-compose</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<version.spring.cloud.azure>7.1.0</version.spring.cloud.azure>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-dependencies</artifactId>
<version>${version.spring.cloud.azure}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-eventhubs</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-docker-compose</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<version.spring.cloud.azure>7.1.0</version.spring.cloud.azure>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-dependencies</artifactId>
<version>${version.spring.cloud.azure}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-messaging-eventhubs</artifactId>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-stream-binder-eventhubs</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-docker-compose</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<version.spring.cloud.azure>7.1.0</version.spring.cloud.azure>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-dependencies</artifactId>
<version>${version.spring.cloud.azure}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-messaging-azure-servicebus</artifactId>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-docker-compose</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<properties>
<version.spring.cloud.azure>7.1.0</version.spring.cloud.azure>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-dependencies</artifactId>
<version>${version.spring.cloud.azure}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-stream-binder-servicebus</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-autoconfigure</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-docker-compose</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
storage-compose.yaml:
services:
storage:
image: mcr.microsoft.com/azure-storage/azurite:latest
ports:
- '10000'
- '10001'
- '10002'
command: azurite -l /data --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --skipApiVersionCheck
storage-compose.yaml:
services:
storage:
image: mcr.microsoft.com/azure-storage/azurite:latest
ports:
- '10000'
- '10001'
- '10002'
command: azurite -l /data --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --skipApiVersionCheck
eventhubs-compose.yaml:
services:
eventhubs:
image: mcr.microsoft.com/azure-messaging/eventhubs-emulator:latest
pull_policy: always
volumes:
# Mount the emulator configuration to the path expected by the emulator image
- "./Config.json:/Eventhubs_Emulator/ConfigFiles/Config.json"
ports:
- "5672"
environment:
# Event Hubs emulator requires external blob/metadata storage provided by azurite
BLOB_SERVER: azurite
METADATA_SERVER: azurite
ACCEPT_EULA: Y
depends_on:
- azurite
networks:
eh-emulator:
aliases:
- "eh-emulator"
azurite:
image: "mcr.microsoft.com/azure-storage/azurite:latest"
ports:
- "10000"
- "10001"
- "10002"
networks:
eh-emulator:
aliases:
- "azurite"
networks:
eh-emulator:
Config.json:
{
"UserConfig": {
"NamespaceConfig": [
{
"Type": "EventHub",
"Name": "emulatorns1",
"Entities": [
{
"Name": "eh1",
"PartitionCount": "2",
"ConsumerGroups": []
}
]
}
],
"LoggingConfig": {
"Type": "File"
}
}
}
eventhubs-compose.yaml:
services:
eventhubs:
image: mcr.microsoft.com/azure-messaging/eventhubs-emulator:latest
pull_policy: always
volumes:
# Mount the emulator configuration to the path expected by the emulator image
- "./Config.json:/Eventhubs_Emulator/ConfigFiles/Config.json"
ports:
- "5672"
environment:
# Event Hubs emulator requires external blob/metadata storage provided by azurite
BLOB_SERVER: azurite
METADATA_SERVER: azurite
ACCEPT_EULA: Y
depends_on:
- azurite
networks:
eh-emulator:
aliases:
- "eh-emulator"
azurite:
image: "mcr.microsoft.com/azure-storage/azurite:latest"
command: "azurite -l /data --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --skipApiVersionCheck"
ports:
- "10000"
- "10001"
- "10002"
networks:
eh-emulator:
aliases:
- "azurite"
networks:
eh-emulator:
Config.json:
{
"UserConfig": {
"NamespaceConfig": [
{
"Type": "EventHub",
"Name": "emulatorns1",
"Entities": [
{
"Name": "eh1",
"PartitionCount": "2",
"ConsumerGroups": []
}
]
}
],
"LoggingConfig": {
"Type": "File"
}
}
}
servicebus-compose.yaml:
services:
servicebus:
image: mcr.microsoft.com/azure-messaging/servicebus-emulator:latest
pull_policy: always
volumes:
- "./Config.json:/ServiceBus_Emulator/ConfigFiles/Config.json"
ports:
- "5672"
environment:
SQL_SERVER: sqledge
MSSQL_SA_PASSWORD: A_Str0ng_Required_Password
ACCEPT_EULA: Y
depends_on:
- sqledge
networks:
sb-emulator:
aliases:
- "sb-emulator"
sqledge:
image: "mcr.microsoft.com/azure-sql-edge:latest"
networks:
sb-emulator:
aliases:
- "sqledge"
environment:
ACCEPT_EULA: Y
MSSQL_SA_PASSWORD: A_Str0ng_Required_Password
networks:
sb-emulator:
Config.json:
{
"UserConfig": {
"Namespaces": [
{
"Name": "sbemulatorns",
"Queues": [
{
"Name": "queue.1",
"Properties": {
"DeadLetteringOnMessageExpiration": false,
"DefaultMessageTimeToLive": "PT1H",
"DuplicateDetectionHistoryTimeWindow": "PT20S",
"ForwardDeadLetteredMessagesTo": "",
"ForwardTo": "",
"LockDuration": "PT1M",
"MaxDeliveryCount": 10,
"RequiresDuplicateDetection": false,
"RequiresSession": false
}
}
],
"Topics": [
{
"Name": "topic.1",
"Properties": {
"DefaultMessageTimeToLive": "PT1H",
"DuplicateDetectionHistoryTimeWindow": "PT20S",
"RequiresDuplicateDetection": false
},
"Subscriptions": [
{
"Name": "subscription.1",
"Properties": {
"DeadLetteringOnMessageExpiration": false,
"DefaultMessageTimeToLive": "PT1H",
"LockDuration": "PT1M",
"MaxDeliveryCount": 10,
"ForwardDeadLetteredMessagesTo": "",
"ForwardTo": "",
"RequiresSession": false
},
"Rules": [
{
"Name": "app-prop-filter-1",
"Properties": {
"FilterType": "Correlation",
"CorrelationFilter": {
"ContentType": "application/text",
"CorrelationId": "id1",
"Label": "subject1",
"MessageId": "msgid1",
"ReplyTo": "someQueue",
"ReplyToSessionId": "sessionId",
"SessionId": "session1",
"To": "xyz"
}
}
}
]
},
{
"Name": "subscription.2",
"Properties": {
"DeadLetteringOnMessageExpiration": false,
"DefaultMessageTimeToLive": "PT1H",
"LockDuration": "PT1M",
"MaxDeliveryCount": 10,
"ForwardDeadLetteredMessagesTo": "",
"ForwardTo": "",
"RequiresSession": false
},
"Rules": [
{
"Name": "user-prop-filter-1",
"Properties": {
"FilterType": "Correlation",
"CorrelationFilter": {
"Properties": {
"prop3": "value3"
}
}
}
}
]
},
{
"Name": "subscription.3",
"Properties": {
"DeadLetteringOnMessageExpiration": false,
"DefaultMessageTimeToLive": "PT1H",
"LockDuration": "PT1M",
"MaxDeliveryCount": 10,
"ForwardDeadLetteredMessagesTo": "",
"ForwardTo": "",
"RequiresSession": false
}
}
]
}
]
}
],
"Logging": {
"Type": "File"
}
}
}
servicebus-compose.yaml:
services:
servicebus:
image: mcr.microsoft.com/azure-messaging/servicebus-emulator:latest
pull_policy: always
volumes:
- "./Config.json:/ServiceBus_Emulator/ConfigFiles/Config.json"
ports:
- "5672"
environment:
SQL_SERVER: sqledge
MSSQL_SA_PASSWORD: A_Str0ng_Required_Password
ACCEPT_EULA: Y
depends_on:
- sqledge
networks:
sb-emulator:
aliases:
- "sb-emulator"
sqledge:
image: "mcr.microsoft.com/azure-sql-edge:latest"
networks:
sb-emulator:
aliases:
- "sqledge"
environment:
ACCEPT_EULA: Y
MSSQL_SA_PASSWORD: A_Str0ng_Required_Password
networks:
sb-emulator:
Config.json:
{
"UserConfig": {
"Namespaces": [
{
"Name": "sbemulatorns",
"Queues": [
{
"Name": "queue.1",
"Properties": {
"DeadLetteringOnMessageExpiration": false,
"DefaultMessageTimeToLive": "PT1H",
"DuplicateDetectionHistoryTimeWindow": "PT20S",
"ForwardDeadLetteredMessagesTo": "",
"ForwardTo": "",
"LockDuration": "PT1M",
"MaxDeliveryCount": 10,
"RequiresDuplicateDetection": false,
"RequiresSession": false
}
}
],
"Topics": [
{
"Name": "topic.1",
"Properties": {
"DefaultMessageTimeToLive": "PT1H",
"DuplicateDetectionHistoryTimeWindow": "PT20S",
"RequiresDuplicateDetection": false
},
"Subscriptions": [
{
"Name": "subscription.1",
"Properties": {
"DeadLetteringOnMessageExpiration": false,
"DefaultMessageTimeToLive": "PT1H",
"LockDuration": "PT1M",
"MaxDeliveryCount": 10,
"ForwardDeadLetteredMessagesTo": "",
"ForwardTo": "",
"RequiresSession": false
},
"Rules": [
{
"Name": "app-prop-filter-1",
"Properties": {
"FilterType": "Correlation",
"CorrelationFilter": {
"ContentType": "application/text",
"CorrelationId": "id1",
"Label": "subject1",
"MessageId": "msgid1",
"ReplyTo": "someQueue",
"ReplyToSessionId": "sessionId",
"SessionId": "session1",
"To": "xyz"
}
}
}
]
},
{
"Name": "subscription.2",
"Properties": {
"DeadLetteringOnMessageExpiration": false,
"DefaultMessageTimeToLive": "PT1H",
"LockDuration": "PT1M",
"MaxDeliveryCount": 10,
"ForwardDeadLetteredMessagesTo": "",
"ForwardTo": "",
"RequiresSession": false
},
"Rules": [
{
"Name": "user-prop-filter-1",
"Properties": {
"FilterType": "Correlation",
"CorrelationFilter": {
"Properties": {
"prop3": "value3"
}
}
}
}
]
},
{
"Name": "subscription.3",
"Properties": {
"DeadLetteringOnMessageExpiration": false,
"DefaultMessageTimeToLive": "PT1H",
"LockDuration": "PT1M",
"MaxDeliveryCount": 10,
"ForwardDeadLetteredMessagesTo": "",
"ForwardTo": "",
"RequiresSession": false
}
}
]
}
]
}
],
"Logging": {
"Type": "File"
}
}
}
@SpringBootTest(properties = {
"spring.docker.compose.skip.in-tests=false",
"spring.docker.compose.file=classpath:storage-compose.yaml",
"spring.docker.compose.stop.command=down"
})
public class AzureBlobResourceDockerComposeTest {
@Value("azure-blob://testcontainers/message.txt")
private Resource blobFile;
@Test
void blobResourceShouldWriteAndReadContent() throws IOException {
String originalContent = "Hello World!";
try (OutputStream os = ((WritableResource) this.blobFile).getOutputStream()) {
os.write(originalContent.getBytes());
}
String resultContent = StreamUtils.copyToString(this.blobFile.getInputStream(), Charset.defaultCharset());
assertThat(resultContent).isEqualTo(originalContent);
}
@Configuration(proxyBeanMethods = false)
@ImportAutoConfiguration(classes = {
AzureGlobalPropertiesAutoConfiguration.class,
AzureStorageBlobAutoConfiguration.class,
AzureStorageBlobResourceAutoConfiguration.class})
static class Config {
}
}
Met spring.docker.compose.filedeze configuratie kunnen gerelateerde bonen in de app communiceren met Blob Storage die wordt uitgevoerd in de Docker-container. Deze actie wordt uitgevoerd door automatisch een AzureStorageBlobConnectionDetails bean te definiëren, die vervolgens wordt gebruikt door de automatische configuratie van Blob Storage, waarbij alle configuratie-eigenschappen met betrekking tot verbindingen worden overschreven.
@SpringBootTest(properties = {
"spring.docker.compose.skip.in-tests=false",
"spring.docker.compose.file=classpath:storage-compose.yaml",
"spring.docker.compose.stop.command=down",
"spring.cloud.azure.storage.queue.queue-name=devstoreaccount1/tc-queue"
})
class StorageQueueDockerComposeTest {
@Autowired
private QueueClient queueClient;
@Test
void queueClientShouldSendAndReceiveMessage() {
String message = "Hello World!";
this.queueClient.create();
this.queueClient.sendMessage(message);
var messageItem = this.queueClient.receiveMessage();
assertThat(messageItem.getBody().toString()).isEqualTo(message);
}
@Configuration
@ImportAutoConfiguration(classes = {
AzureGlobalPropertiesAutoConfiguration.class,
AzureStorageQueueAutoConfiguration.class})
static class Config {
}
}
Met spring.docker.compose.filedeze configuratie kunnen gerelateerde bonen in de app communiceren met Queue Storage die wordt uitgevoerd in de Docker-container. Deze actie wordt uitgevoerd door automatisch een AzureStorageQueueConnectionDetails bean te definiëren, die vervolgens wordt gebruikt door de automatische configuratie van Queue Storage, waarbij eventuele verbindingsgerelateerde configuratie-eigenschappen worden overschreven.
@SpringBootTest(properties = {
"spring.docker.compose.skip.in-tests=false",
"spring.docker.compose.file=classpath:eventhubs-compose.yaml",
"spring.docker.compose.stop.command=down",
"spring.docker.compose.readiness.timeout=PT5M",
"spring.cloud.azure.eventhubs.event-hub-name=eh1",
"spring.cloud.azure.eventhubs.producer.event-hub-name=eh1"
})
class EventHubsDockerComposeTest {
@Autowired
private AzureEventHubsConnectionDetails connectionDetails;
@Autowired
private EventHubProducerClient producerClient;
@Test
void connectionDetailsShouldBeProvidedByFactory() {
assertThat(connectionDetails).isNotNull();
assertThat(connectionDetails.getConnectionString())
.isNotBlank()
.startsWith("Endpoint=sb://");
}
@Test
void producerClientCanSendMessage() {
// Wait for Event Hubs emulator to be fully ready and event hub entity to be available
waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -> {
EventData event = new EventData("Hello World!");
this.producerClient.send(Collections.singletonList(event));
});
}
@Configuration(proxyBeanMethods = false)
@ImportAutoConfiguration(classes = {
AzureGlobalPropertiesAutoConfiguration.class,
AzureEventHubsAutoConfiguration.class})
static class Config {
}
}
Met spring.docker.compose.filedeze configuratie kunnen gerelateerde bonen in de app communiceren met Event Hubs die worden uitgevoerd in de Docker-container. Deze actie wordt uitgevoerd door automatisch een AzureEventHubsConnectionDetails bean te definiëren, die vervolgens wordt gebruikt door de automatische configuratie van Event Hubs, waarbij alle configuratie-eigenschappen met betrekking tot verbindingen worden overschreven.
@SpringBootTest(properties = {
"spring.docker.compose.skip.in-tests=false",
"spring.docker.compose.file=classpath:eventhubs-compose.yaml",
"spring.docker.compose.stop.command=down",
"spring.docker.compose.readiness.timeout=PT5M",
"spring.cloud.function.definition=consume;supply",
"spring.cloud.stream.bindings.consume-in-0.destination=eh1",
"spring.cloud.stream.bindings.consume-in-0.group=$Default",
"spring.cloud.stream.bindings.supply-out-0.destination=eh1",
"spring.cloud.stream.eventhubs.bindings.consume-in-0.consumer.checkpoint.mode=MANUAL",
"spring.cloud.stream.poller.fixed-delay=1000",
"spring.cloud.stream.poller.initial-delay=0"
})
class EventHubsDockerComposeTest {
private static final Logger LOGGER = LoggerFactory.getLogger(EventHubsDockerComposeTest.class);
private static final Set<String> RECEIVED_MESSAGES = ConcurrentHashMap.newKeySet();
private static final AtomicInteger MESSAGE_SEQUENCE = new AtomicInteger(0);
@Test
void supplierAndConsumerShouldWorkThroughEventHub() {
waitAtMost(Duration.ofSeconds(120))
.pollDelay(Duration.ofSeconds(2))
.pollInterval(Duration.ofSeconds(2))
.untilAsserted(() -> {
assertThat(RECEIVED_MESSAGES).isNotEmpty();
LOGGER.info("✓ Test passed - Consumer received {} message(s)", RECEIVED_MESSAGES.size());
});
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@ImportAutoConfiguration(classes = {
AzureGlobalPropertiesAutoConfiguration.class,
AzureEventHubsAutoConfiguration.class,
AzureEventHubsMessagingAutoConfiguration.class})
static class Config {
private static final String CHECKPOINT_CONTAINER_NAME = "eventhubs-checkpoint";
@Bean
public BlobCheckpointStore blobCheckpointStore(AzureStorageBlobConnectionDetails connectionDetails) {
BlobServiceAsyncClient blobServiceAsyncClient = new BlobServiceClientBuilder()
.connectionString(connectionDetails.getConnectionString())
.buildAsyncClient();
BlobContainerAsyncClient containerAsyncClient = blobServiceAsyncClient
.getBlobContainerAsyncClient(CHECKPOINT_CONTAINER_NAME);
if (Boolean.FALSE.equals(containerAsyncClient.exists().block(Duration.ofSeconds(3)))) {
containerAsyncClient.create().block(Duration.ofSeconds(3));
}
return new BlobCheckpointStore(containerAsyncClient);
}
@Bean
public Supplier<Message<String>> supply() {
return () -> {
int sequence = MESSAGE_SEQUENCE.getAndIncrement();
String payload = "Hello world, " + sequence;
LOGGER.info("[Supplier] Invoked - message sequence: {}", sequence);
return MessageBuilder.withPayload(payload).build();
};
}
@Bean
public Consumer<Message<String>> consume() {
return message -> {
String payload = message.getPayload();
RECEIVED_MESSAGES.add(payload);
LOGGER.info("[Consumer] Received message: {}", payload);
Checkpointer checkpointer = (Checkpointer) message.getHeaders().get(CHECKPOINTER);
if (checkpointer != null) {
checkpointer.success()
.doOnSuccess(s -> LOGGER.info("[Consumer] Message checkpointed"))
.doOnError(e -> LOGGER.error("[Consumer] Checkpoint failed", e))
.block();
}
};
}
}
}
Met spring.docker.compose.filedeze configuratie kunnen gerelateerde bonen in de app communiceren met Event Hubs die worden uitgevoerd in de Docker-container. Deze actie wordt uitgevoerd door automatisch een AzureEventHubsConnectionDetails bean te definiëren, die vervolgens wordt gebruikt door de automatische configuratie van Event Hubs, waarbij alle configuratie-eigenschappen met betrekking tot verbindingen worden overschreven.
@SpringBootTest(properties = {
"spring.docker.compose.skip.in-tests=false",
"spring.docker.compose.file=classpath:servicebus-compose.yaml",
"spring.docker.compose.stop.command=down",
"spring.docker.compose.readiness.timeout=PT5M",
"spring.cloud.azure.servicebus.namespace=sbemulatorns",
"spring.cloud.azure.servicebus.entity-name=queue.1",
"spring.cloud.azure.servicebus.entity-type=queue",
"spring.cloud.azure.servicebus.producer.entity-name=queue.1",
"spring.cloud.azure.servicebus.producer.entity-type=queue",
"spring.cloud.azure.servicebus.processor.entity-name=queue.1",
"spring.cloud.azure.servicebus.processor.entity-type=queue"
})
class ServiceBusDockerComposeTest {
@Autowired
private AzureServiceBusConnectionDetails connectionDetails;
@Autowired
private ServiceBusSenderClient senderClient;
@Autowired
private ServiceBusTemplate serviceBusTemplate;
@Test
void connectionDetailsShouldBeProvidedByFactory() {
assertThat(connectionDetails).isNotNull();
assertThat(connectionDetails.getConnectionString())
.isNotBlank()
.startsWith("Endpoint=sb://");
}
@Test
void senderClientCanSendMessage() {
// Wait for Service Bus emulator to be fully ready and queue entity to be available
// The emulator depends on SQL Edge and needs time to initialize the messaging entities
waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -> {
this.senderClient.sendMessage(new ServiceBusMessage("Hello World!"));
});
waitAtMost(Duration.ofSeconds(30)).pollDelay(Duration.ofSeconds(5)).untilAsserted(() -> {
assertThat(Config.MESSAGES).contains("Hello World!");
});
}
@Test
void serviceBusTemplateCanSendMessage() {
// Wait for Service Bus emulator to be fully ready and queue entity to be available
// The emulator depends on SQL Edge and needs time to initialize the messaging entities
waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -> {
this.serviceBusTemplate.sendAsync("queue.1",
MessageBuilder.withPayload("Hello from ServiceBusTemplate!").build()).block(Duration.ofSeconds(10));
});
waitAtMost(Duration.ofSeconds(30)).pollDelay(Duration.ofSeconds(5)).untilAsserted(() -> {
assertThat(Config.MESSAGES).contains("Hello from ServiceBusTemplate!");
});
}
@Configuration(proxyBeanMethods = false)
@ImportAutoConfiguration(classes = {
AzureGlobalPropertiesAutoConfiguration.class,
AzureServiceBusAutoConfiguration.class,
AzureServiceBusMessagingAutoConfiguration.class})
static class Config {
private static final Set<String> MESSAGES = ConcurrentHashMap.newKeySet();
@Bean
ServiceBusRecordMessageListener processMessage() {
return context -> {
MESSAGES.add(context.getMessage().getBody().toString());
};
}
@Bean
ServiceBusErrorHandler errorHandler() {
// No-op error handler for tests: acknowledge errors without affecting test execution.
return (context) -> {
};
}
}
}
Met spring.docker.compose.filedeze configuratie kunnen gerelateerde bonen in de app communiceren met Service Bus die wordt uitgevoerd in de Docker-container. Deze actie wordt uitgevoerd door automatisch een AzureServiceBusConnectionDetails bean te definiëren, die vervolgens wordt gebruikt door de automatische configuratie van Service Bus, waarbij alle configuratie-eigenschappen met betrekking tot verbindingen worden overschreven.
@SpringBootTest(properties = {
"spring.docker.compose.skip.in-tests=false",
"spring.docker.compose.file=classpath:servicebus-compose.yaml",
"spring.docker.compose.stop.command=down",
"spring.cloud.function.definition=consume;supply",
"spring.cloud.stream.bindings.consume-in-0.destination=queue.1",
"spring.cloud.stream.bindings.supply-out-0.destination=queue.1",
"spring.cloud.stream.servicebus.bindings.consume-in-0.consumer.auto-complete=false",
"spring.cloud.stream.servicebus.bindings.supply-out-0.producer.entity-type=queue",
"spring.cloud.stream.poller.fixed-delay=1000",
"spring.cloud.stream.poller.initial-delay=0"
})
class ServiceBusDockerComposeTest {
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceBusDockerComposeTest.class);
private static final Set<String> RECEIVED_MESSAGES = ConcurrentHashMap.newKeySet();
private static final AtomicInteger MESSAGE_SEQUENCE = new AtomicInteger(0);
@Test
void supplierAndConsumerShouldWorkThroughServiceBusQueue() {
waitAtMost(Duration.ofSeconds(60))
.pollDelay(Duration.ofSeconds(2))
.untilAsserted(() -> {
assertThat(RECEIVED_MESSAGES).isNotEmpty();
LOGGER.info("✓ Test passed - Consumer received {} message(s)", RECEIVED_MESSAGES.size());
});
}
@Configuration(proxyBeanMethods = false)
@EnableAutoConfiguration
@ImportAutoConfiguration(classes = {
AzureGlobalPropertiesAutoConfiguration.class,
AzureServiceBusAutoConfiguration.class,
AzureServiceBusMessagingAutoConfiguration.class})
static class Config {
@Bean
public Supplier<Message<String>> supply() {
return () -> {
int sequence = MESSAGE_SEQUENCE.getAndIncrement();
String payload = "Hello world, " + sequence;
LOGGER.info("[Supplier] Invoked - message sequence: {}", sequence);
return MessageBuilder.withPayload(payload).build();
};
}
@Bean
public Consumer<Message<String>> consume() {
return message -> {
String payload = message.getPayload();
RECEIVED_MESSAGES.add(payload);
LOGGER.info("[Consumer] Received message: {}", payload);
Checkpointer checkpointer = (Checkpointer) message.getHeaders().get(CHECKPOINTER);
if (checkpointer != null) {
checkpointer.success()
.doOnSuccess(s -> LOGGER.info("[Consumer] Message checkpointed"))
.doOnError(e -> LOGGER.error("[Consumer] Checkpoint failed", e))
.block();
}
};
}
}
}
Met spring.docker.compose.filedeze configuratie kunnen gerelateerde bonen in de app communiceren met Service Bus die wordt uitgevoerd in de Docker-container. Deze actie wordt uitgevoerd door automatisch een AzureServiceBusConnectionDetails bean te definiëren, die vervolgens wordt gebruikt door de automatische configuratie van Service Bus, waarbij alle configuratie-eigenschappen met betrekking tot verbindingen worden overschreven.