Подключение службы — это подключение к любой удаленной службе. Автонастройка Spring Boot может использовать сведения о подключении к службе и использовать их для установки подключения к удаленной службе. При этом сведения о подключении имеют приоритет над любыми свойствами конфигурации, связанными с подключением.
При использовании Testcontainers можно автоматически создавать сведения о подключении для службы, работающей в контейнере, заметив поле контейнера в тестовом классе.
В следующей таблице приведены сведения о классах фабрики сведений о подключении, поддерживаемых в jar-файле spring-cloud-azure-testcontainers:
<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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-cosmos</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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-storage-blob</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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-storage-queue</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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter-eventhubs</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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-stream-binder-eventhubs</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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-starter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-messaging-azure-servicebus</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</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>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-testcontainers</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.azure.spring</groupId>
<artifactId>spring-cloud-azure-stream-binder-servicebus</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.microsoft.sqlserver</groupId>
<artifactId>mssql-jdbc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
@SpringBootTest(classes = CosmosTestcontainersTest.class)
@Testcontainers
@ExtendWith(SpringExtension.class)
@ImportAutoConfiguration(classes = { AzureGlobalPropertiesAutoConfiguration.class, AzureCosmosAutoConfiguration.class})
public class CosmosTestcontainersTest {
@TempDir
private static File tempFolder;
@Autowired
private CosmosClient client;
@Container
@ServiceConnection
static CosmosDBEmulatorContainer cosmos = new CosmosDBEmulatorContainer(
DockerImageName.parse("mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator:latest"))
.waitingFor(Wait.forHttps("/_explorer/emulator.pem").forStatusCode(200).allowInsecure())
.withStartupTimeout(Duration.ofMinutes(3));
@BeforeAll
public static void setup() throws IOException, CertificateException, KeyStoreException, NoSuchAlgorithmException {
Path keyStoreFile = new File(tempFolder, "azure-cosmos-emulator.keystore").toPath();
KeyStore keyStore = cosmos.buildNewKeyStore();
try (var out = Files.newOutputStream(keyStoreFile.toFile().toPath())) {
keyStore.store(out, cosmos.getEmulatorKey().toCharArray());
}
System.setProperty("javax.net.ssl.trustStore", keyStoreFile.toString());
System.setProperty("javax.net.ssl.trustStorePassword", cosmos.getEmulatorKey());
System.setProperty("javax.net.ssl.trustStoreType", "PKCS12");
}
@Test
public void test() {
CosmosDatabaseResponse databaseResponse = client.createDatabaseIfNotExists("Azure");
assertThat(databaseResponse.getStatusCode()).isEqualTo(201);
CosmosContainerResponse containerResponse = client
.getDatabase("Azure")
.createContainerIfNotExists("ServiceContainer", "/name");
assertThat(containerResponse.getStatusCode()).isEqualTo(201);
}
}
Чтобы использовать CosmosDBEmulatorContainer, необходимо подготовить KeyStore протокол TLS/SSL. Дополнительные сведения см. в модуля Azure Cosmos DB в документации testcontainers. С помощью @ServiceConnectionэта конфигурация позволяет бобам, связанным с Cosmos DB, взаимодействовать с Cosmos DB, работающей в контейнере Docker, управляемом Testcontainers. Эта настройка автоматически определяет боб, который автоматически настраивается AzureCosmosConnectionDetails Cosmos DB, а затем используется для переопределения свойств конфигурации, связанных с подключением.
@SpringJUnitConfig
@Testcontainers
class StorageBlobTestcontainersTest {
@Container
@ServiceConnection
private static final GenericContainer<?> AZURITE_CONTAINER = new GenericContainer<>(
"mcr.microsoft.com/azure-storage/azurite:latest")
.withExposedPorts(10000)
.withCommand("azurite --skipApiVersionCheck && azurite -l /data --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0");
@Value("azure-blob://testcontainers/message.txt")
private Resource blobFile;
@Test
void test() 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 {
}
}
С помощью @ServiceConnectionэта конфигурация позволяет бобам, связанным с BLOB-объектами, взаимодействовать с хранилищем BLOB-объектов, работающим в контейнере Docker, управляемом Testcontainers. Эта настройка автоматически определяет боб, который автоматически настраивается AzureStorageBlobConnectionDetails хранилище BLOB-объектов, а затем используется для переопределения свойств конфигурации, связанных с подключением.
@SpringBootTest(classes = StorageQueueTestcontainersTest.class)
@Testcontainers
@TestPropertySource(properties = "spring.cloud.azure.storage.queue.queue-name=devstoreaccount1/tc-queue")
@ExtendWith(SpringExtension.class)
@ImportAutoConfiguration(classes = { AzureGlobalPropertiesAutoConfiguration.class, AzureStorageQueueAutoConfiguration.class})
public class StorageQueueTestcontainersTest {
@Container
@ServiceConnection
private static final GenericContainer<?> AZURITE_CONTAINER = new GenericContainer<>(
"mcr.microsoft.com/azure-storage/azurite:latest")
.withExposedPorts(10001)
.withCommand("azurite-queue", "--queueHost", "0.0.0.0", "--skipApiVersionCheck");
@Autowired
private QueueClient queueClient;
@BeforeAll
public static void setup() {
AZURITE_CONTAINER.start();
}
@Test
public void test() {
String message = "Hello World!";
this.queueClient.create();
this.queueClient.sendMessage(message);
assertThat(this.queueClient.receiveMessage().getBody().toString()).isEqualTo(message);
}
}
С помощью @ServiceConnectionэта конфигурация позволяет бобам, связанным с очередью, взаимодействовать с хранилищем очередей, работающим в контейнере Docker, управляемом Testcontainers. Эта настройка автоматически определяет боб, который автоматически настраивается AzureStorageQueueConnectionDetails хранилище очередей, а затем используется для переопределения любых свойств конфигурации, связанных с подключением.
@SpringJUnitConfig
@TestPropertySource(properties = { "spring.cloud.azure.eventhubs.event-hub-name=eh1",
"spring.cloud.azure.eventhubs.producer.event-hub-name=eh1" })
@Testcontainers
class EventHubsTestContainerTest {
private static final Network NETWORK = Network.newNetwork();
private static final AzuriteContainer AZURITE = new AzuriteContainer(
"mcr.microsoft.com/azure-storage/azurite:latest")
.withNetwork(NETWORK)
.withNetworkAliases("azurite");
@Container
@ServiceConnection
private static final EventHubsEmulatorContainer EVENT_HUBS = new EventHubsEmulatorContainer(
"mcr.microsoft.com/azure-messaging/eventhubs-emulator:latest")
.acceptLicense()
.withCopyFileToContainer(MountableFile.forClasspathResource("Config.json"),
"/Eventhubs_Emulator/ConfigFiles/Config.json")
.withNetwork(NETWORK)
.withAzuriteContainer(AZURITE);
@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 {
}
}
Эта @ServiceConnectionконфигурация позволяет связанным бобам в приложении взаимодействовать с центрами событий, работающими в контейнере Docker, управляемом Testcontainers. Эта настройка автоматически определяет боб, который автоматически настраивается AzureEventHubsConnectionDetails центрами событий, а затем используется для переопределения любых свойств конфигурации, связанных с подключением.
@SpringJUnitConfig
@TestPropertySource(properties = {
"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"})
@Testcontainers
class EventHubsTestContainerTest {
private static final Network NETWORK = Network.newNetwork();
private static final AzuriteContainer AZURITE = new AzuriteContainer(
"mcr.microsoft.com/azure-storage/azurite:latest")
.withCommand("azurite", "--blobHost", "0.0.0.0", "--queueHost", "0.0.0.0", "--tableHost", "0.0.0.0",
"--skipApiVersionCheck")
.withNetwork(NETWORK)
.withNetworkAliases("azurite");
@Container
@ServiceConnection
private static final EventHubsEmulatorContainer EVENT_HUBS = new EventHubsEmulatorContainer(
"mcr.microsoft.com/azure-messaging/eventhubs-emulator:latest")
.acceptLicense()
.withCopyFileToContainer(MountableFile.forClasspathResource("Config.json"),
"/Eventhubs_Emulator/ConfigFiles/Config.json")
.withNetwork(NETWORK)
.withAzuriteContainer(AZURITE);
private static final Logger LOGGER = LoggerFactory.getLogger(EventHubsTestContainerTest.class);
private static final Set<String> RECEIVED_MESSAGES = ConcurrentHashMap.newKeySet();
private static final AtomicInteger MESSAGE_SEQUENCE = new AtomicInteger(0);
@Test
void supplierAndConsumerShouldWorkThroughEventHubs() {
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() {
BlobServiceAsyncClient blobServiceAsyncClient = new BlobServiceClientBuilder()
.connectionString(AZURITE.getConnectionString())
.serviceVersion(BlobServiceVersion.V2025_01_05)
.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();
}
};
}
}
}
Эта @ServiceConnectionконфигурация позволяет связанным бобам в приложении взаимодействовать с центрами событий, работающими в контейнере Docker, управляемом Testcontainers. Эта настройка автоматически определяет боб, который автоматически настраивается AzureEventHubsConnectionDetails центрами событий, а затем используется для переопределения любых свойств конфигурации, связанных с подключением.
@SpringJUnitConfig
@TestPropertySource(properties = { "spring.cloud.azure.servicebus.entity-name=queue.1",
"spring.cloud.azure.servicebus.entity-type=queue" })
@Testcontainers
class ServiceBusTestContainerTest {
private static final Network NETWORK = Network.newNetwork();
private static final MSSQLServerContainer<?> SQLSERVER = new MSSQLServerContainer<>(
"mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04")
.acceptLicense()
.withNetwork(NETWORK)
.withNetworkAliases("sqlserver");
@Container
@ServiceConnection
private static final ServiceBusEmulatorContainer SERVICE_BUS = new ServiceBusEmulatorContainer(
"mcr.microsoft.com/azure-messaging/servicebus-emulator:latest")
.acceptLicense()
.withCopyFileToContainer(MountableFile.forClasspathResource("Config.json"),
"/ServiceBus_Emulator/ConfigFiles/Config.json")
.withNetwork(NETWORK)
.withMsSqlServerContainer(SQLSERVER);
@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
waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -> {
this.senderClient.sendMessage(new ServiceBusMessage("Hello World!"));
});
waitAtMost(Duration.ofSeconds(30)).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
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)).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) -> {
};
}
}
}
Эта @ServiceConnectionконфигурация позволяет связанным бобам в приложении взаимодействовать с служебной шиной, работающей в контейнере Docker, управляемом Testcontainers. Эта настройка автоматически определяет боб, который автоматически настраивается служебной AzureServiceBusConnectionDetails шины, а затем используется для переопределения свойств конфигурации, связанных с подключением.
@SpringJUnitConfig
@TestPropertySource(properties = {
"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"})
@Testcontainers
class ServiceBusTestContainerTest {
private static final Network NETWORK = Network.newNetwork();
private static final MSSQLServerContainer<?> SQLSERVER = new MSSQLServerContainer<>(
"mcr.microsoft.com/mssql/server:2022-CU14-ubuntu-22.04")
.acceptLicense()
.withNetwork(NETWORK)
.withNetworkAliases("sqlserver");
@Container
@ServiceConnection
private static final ServiceBusEmulatorContainer SERVICE_BUS = new ServiceBusEmulatorContainer(
"mcr.microsoft.com/azure-messaging/servicebus-emulator:latest")
.acceptLicense()
.withCopyFileToContainer(MountableFile.forClasspathResource("Config.json"),
"/ServiceBus_Emulator/ConfigFiles/Config.json")
.withNetwork(NETWORK)
.withMsSqlServerContainer(SQLSERVER);
private static final Logger LOGGER = LoggerFactory.getLogger(ServiceBusTestContainerTest.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();
}
};
}
}
}
Эта @ServiceConnectionконфигурация позволяет связанным бобам в приложении взаимодействовать с служебной шиной, работающей в контейнере Docker, управляемом Testcontainers. Эта настройка автоматически определяет боб, который автоматически настраивается служебной AzureServiceBusConnectionDetails шины, а затем используется для переопределения свойств конфигурации, связанных с подключением.