{"id":3636,"date":"2026-03-30T08:00:31","date_gmt":"2026-03-30T15:00:31","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/azure-sdk\/?p=3636"},"modified":"2026-03-28T00:22:14","modified_gmt":"2026-03-28T07:22:14","slug":"writing-azure-service-related-unit-tests-with-docker-using-spring-cloud-azure","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/azure-sdk\/writing-azure-service-related-unit-tests-with-docker-using-spring-cloud-azure\/","title":{"rendered":"Writing Azure service-related unit tests with Docker using Spring Cloud Azure"},"content":{"rendered":"<p>In this post, we explore how to write Azure service-related unit tests for Spring Boot projects using Docker and Spring Cloud Azure.<\/p>\n<h2>What is Spring Cloud Azure?<\/h2>\n<p>Spring Cloud Azure is an open-source project that makes it easier to use Azure services in Spring applications. For more information about this topic, see the <a href=\"https:\/\/learn.microsoft.com\/azure\/developer\/java\/spring-framework\/spring-cloud-azure-overview\">user guide<\/a>.<\/p>\n<h2>Why use Docker for unit testing?<\/h2>\n<p>Using Docker for unit testing provides an easy way to test Azure-related features without provisioning real Azure resources. It helps you quickly verify your code changes locally and ensures that your application works as expected before deploying to Azure.<\/p>\n<h2>Examples<\/h2>\n<h3>Azure Storage Blob<\/h3>\n<h4>Using Docker Compose<\/h4>\n<ol>\n<li>Add the following dependency to your <code>pom.xml<\/code> file:<\/li>\n<\/ol>\n<pre><code class=\"language-xml\">  &lt;properties&gt;\r\n    &lt;version.spring.cloud.azure&gt;7.1.0&lt;\/version.spring.cloud.azure&gt;\r\n  &lt;\/properties&gt;\r\n\r\n  &lt;dependencyManagement&gt;\r\n    &lt;dependencies&gt;\r\n      &lt;dependency&gt;\r\n        &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;spring-cloud-azure-dependencies&lt;\/artifactId&gt;\r\n        &lt;version&gt;${version.spring.cloud.azure}&lt;\/version&gt;\r\n        &lt;type&gt;pom&lt;\/type&gt;\r\n        &lt;scope&gt;import&lt;\/scope&gt;\r\n      &lt;\/dependency&gt;\r\n    &lt;\/dependencies&gt;\r\n  &lt;\/dependencyManagement&gt;\r\n\r\n  &lt;dependencies&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-starter-storage-blob&lt;\/artifactId&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-boot-starter-test&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-docker-compose&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n  &lt;\/dependencies&gt;<\/code><\/pre>\n<ol start=\"2\">\n<li>Create a <code>storage-compose.yaml<\/code> file in the <code>src\/test\/resources<\/code> directory with the following content:<\/li>\n<\/ol>\n<pre><code class=\"language-yaml\">services:\r\n  storage:\r\n    image: mcr.microsoft.com\/azure-storage\/azurite:latest\r\n    ports:\r\n      - '10000'\r\n      - '10001'\r\n      - '10002'\r\n    command: azurite -l \/data --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0 --skipApiVersionCheck<\/code><\/pre>\n<ol start=\"3\">\n<li>Write a unit test that uses the Docker container:<\/li>\n<\/ol>\n<pre><code class=\"language-java\">import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.storage.blob.AzureStorageBlobAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.storage.blob.AzureStorageBlobResourceAutoConfiguration;\r\nimport org.junit.jupiter.api.Test;\r\nimport org.springframework.beans.factory.annotation.Value;\r\nimport org.springframework.boot.autoconfigure.ImportAutoConfiguration;\r\nimport org.springframework.boot.test.context.SpringBootTest;\r\nimport org.springframework.context.annotation.Configuration;\r\nimport org.springframework.core.io.Resource;\r\nimport org.springframework.core.io.WritableResource;\r\nimport org.springframework.util.StreamUtils;\r\n\r\nimport java.io.IOException;\r\nimport java.io.OutputStream;\r\nimport java.nio.charset.Charset;\r\n\r\nimport static org.assertj.core.api.Assertions.assertThat;\r\n\r\n@SpringBootTest(properties = {\r\n        \"spring.docker.compose.skip.in-tests=false\",\r\n        \"spring.docker.compose.file=classpath:storage-compose.yaml\",\r\n        \"spring.docker.compose.stop.command=down\"\r\n})\r\npublic class AzureBlobResourceDockerComposeTest {\r\n\r\n    @Value(\"azure-blob:\/\/testcontainers\/message.txt\")\r\n    private Resource blobFile;\r\n\r\n    @Test\r\n    void test() throws IOException {\r\n        String originalContent = \"Hello World!\";\r\n        try (OutputStream os = ((WritableResource) this.blobFile).getOutputStream()) {\r\n            os.write(originalContent.getBytes());\r\n        }\r\n        String resultContent = StreamUtils.copyToString(this.blobFile.getInputStream(), Charset.defaultCharset());\r\n        assertThat(resultContent).isEqualTo(originalContent);\r\n    }\r\n\r\n    @Configuration(proxyBeanMethods = false)\r\n    @ImportAutoConfiguration(classes = {\r\n            AzureGlobalPropertiesAutoConfiguration.class,\r\n            AzureStorageBlobAutoConfiguration.class,\r\n            AzureStorageBlobResourceAutoConfiguration.class})\r\n    static class Config {\r\n    }\r\n}<\/code><\/pre>\n<p>In this example, we start a Docker container running Azurite, an open-source Azure Storage emulator. The unit test writes a string to a blob in the emulator and then reads it back to verify that the content is correct.<\/p>\n<h4>Using Testcontainers<\/h4>\n<ol>\n<li>Add the following dependency to your <code>pom.xml<\/code> file:<\/li>\n<\/ol>\n<pre><code class=\"language-xml\">  &lt;properties&gt;\r\n    &lt;version.spring.cloud.azure&gt;7.1.0&lt;\/version.spring.cloud.azure&gt;\r\n  &lt;\/properties&gt;\r\n\r\n  &lt;dependencyManagement&gt;\r\n    &lt;dependencies&gt;\r\n      &lt;dependency&gt;\r\n        &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;spring-cloud-azure-dependencies&lt;\/artifactId&gt;\r\n        &lt;version&gt;${version.spring.cloud.azure}&lt;\/version&gt;\r\n        &lt;type&gt;pom&lt;\/type&gt;\r\n        &lt;scope&gt;import&lt;\/scope&gt;\r\n      &lt;\/dependency&gt;\r\n    &lt;\/dependencies&gt;\r\n  &lt;\/dependencyManagement&gt;\r\n\r\n  &lt;dependencies&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-starter-storage-blob&lt;\/artifactId&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-boot-starter-test&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-boot-testcontainers&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;org.testcontainers&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;testcontainers-junit-jupiter&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-testcontainers&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n  &lt;\/dependencies&gt;<\/code><\/pre>\n<ol start=\"2\">\n<li>Write a unit test that uses Testcontainers:<\/li>\n<\/ol>\n<pre><code class=\"language-java\">import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.storage.blob.AzureStorageBlobAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.storage.blob.AzureStorageBlobResourceAutoConfiguration;\r\nimport org.junit.jupiter.api.Test;\r\nimport org.springframework.beans.factory.annotation.Value;\r\nimport org.springframework.boot.autoconfigure.ImportAutoConfiguration;\r\nimport org.springframework.boot.testcontainers.service.connection.ServiceConnection;\r\nimport org.springframework.context.annotation.Configuration;\r\nimport org.springframework.core.io.Resource;\r\nimport org.springframework.core.io.WritableResource;\r\nimport org.springframework.test.context.junit.jupiter.SpringJUnitConfig;\r\nimport org.springframework.util.StreamUtils;\r\nimport org.testcontainers.containers.GenericContainer;\r\nimport org.testcontainers.junit.jupiter.Container;\r\nimport org.testcontainers.junit.jupiter.Testcontainers;\r\n\r\nimport java.io.IOException;\r\nimport java.io.OutputStream;\r\nimport java.nio.charset.Charset;\r\n\r\nimport static org.assertj.core.api.Assertions.assertThat;\r\n\r\n@SpringJUnitConfig\r\n@Testcontainers\r\nclass AzureBlobResourceTestContainerTest {\r\n    @Container\r\n    @ServiceConnection\r\n    private static final GenericContainer&lt;?&gt; AZURITE_CONTAINER = new GenericContainer&lt;&gt;(\r\n            \"mcr.microsoft.com\/azure-storage\/azurite:latest\")\r\n            .withExposedPorts(10000)\r\n            .withCommand(\"azurite --skipApiVersionCheck &amp;&amp; azurite -l \/data --blobHost 0.0.0.0 --queueHost 0.0.0.0 --tableHost 0.0.0.0\");\r\n\r\n    @Value(\"azure-blob:\/\/testcontainers\/message.txt\")\r\n    private Resource blobFile;\r\n\r\n    @Test\r\n    void test() throws IOException {\r\n        String originalContent = \"Hello World!\";\r\n        try (OutputStream os = ((WritableResource) this.blobFile).getOutputStream()) {\r\n            os.write(originalContent.getBytes());\r\n        }\r\n        String resultContent = StreamUtils.copyToString(this.blobFile.getInputStream(), Charset.defaultCharset());\r\n        assertThat(resultContent).isEqualTo(originalContent);\r\n    }\r\n\r\n    @Configuration(proxyBeanMethods = false)\r\n    @ImportAutoConfiguration(classes = {AzureGlobalPropertiesAutoConfiguration.class, AzureStorageBlobAutoConfiguration.class, AzureStorageBlobResourceAutoConfiguration.class})\r\n    static class Config {\r\n    }\r\n}<\/code><\/pre>\n<p>In this example, we start a Docker container running Azurite with Testcontainers. The unit test is similar to the previous example: it writes a string to a blob and then reads it back to verify the content.<\/p>\n<h3>Azure Service Bus<\/h3>\n<h4>Spring messaging with Service Bus<\/h4>\n<h5>Using Docker Compose<\/h5>\n<ol>\n<li>Add the following dependency to your <code>pom.xml<\/code> file:<\/li>\n<\/ol>\n<pre><code class=\"language-xml\">  &lt;properties&gt;\r\n    &lt;version.spring.cloud.azure&gt;7.1.0&lt;\/version.spring.cloud.azure&gt;\r\n  &lt;\/properties&gt;\r\n\r\n  &lt;dependencyManagement&gt;\r\n    &lt;dependencies&gt;\r\n      &lt;dependency&gt;\r\n        &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;spring-cloud-azure-dependencies&lt;\/artifactId&gt;\r\n        &lt;version&gt;${version.spring.cloud.azure}&lt;\/version&gt;\r\n        &lt;type&gt;pom&lt;\/type&gt;\r\n        &lt;scope&gt;import&lt;\/scope&gt;\r\n      &lt;\/dependency&gt;\r\n    &lt;\/dependencies&gt;\r\n  &lt;\/dependencyManagement&gt;\r\n\r\n  &lt;dependencies&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-messaging-azure-servicebus&lt;\/artifactId&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-starter&lt;\/artifactId&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-boot-starter-test&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-docker-compose&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n  &lt;\/dependencies&gt;<\/code><\/pre>\n<ol start=\"2\">\n<li>Create a <code>servicebus-compose.yaml<\/code> file in the <code>src\/test\/resources<\/code> directory with the following content:<\/li>\n<\/ol>\n<pre><code class=\"language-yaml\">services:\r\n  servicebus:\r\n    image: mcr.microsoft.com\/azure-messaging\/servicebus-emulator:latest\r\n    pull_policy: always\r\n    volumes:\r\n      - \".\/Config.json:\/ServiceBus_Emulator\/ConfigFiles\/Config.json\"\r\n    ports:\r\n      - \"5672\"\r\n    environment:\r\n      SQL_SERVER: sqledge\r\n      MSSQL_SA_PASSWORD: A_Str0ng_Required_Password\r\n      ACCEPT_EULA: Y\r\n    depends_on:\r\n      - sqledge\r\n    networks:\r\n      sb-emulator:\r\n        aliases:\r\n          - \"sb-emulator\"\r\n  sqledge:\r\n    image: \"mcr.microsoft.com\/azure-sql-edge:latest\"\r\n    networks:\r\n      sb-emulator:\r\n        aliases:\r\n          - \"sqledge\"\r\n    environment:\r\n      ACCEPT_EULA: Y\r\n      MSSQL_SA_PASSWORD: A_Str0ng_Required_Password\r\n\r\nnetworks:\r\n  sb-emulator:<\/code><\/pre>\n<ol start=\"3\">\n<li>Create a <code>Config.json<\/code> file in the <code>src\/test\/resources<\/code> directory with the following content:<\/li>\n<\/ol>\n<pre><code class=\"language-json\">{\r\n  \"UserConfig\": {\r\n    \"Namespaces\": [\r\n      {\r\n        \"Name\": \"sbemulatorns\",\r\n        \"Queues\": [\r\n          {\r\n            \"Name\": \"queue.1\",\r\n            \"Properties\": {\r\n              \"DeadLetteringOnMessageExpiration\": false,\r\n              \"DefaultMessageTimeToLive\": \"PT1H\",\r\n              \"DuplicateDetectionHistoryTimeWindow\": \"PT20S\",\r\n              \"ForwardDeadLetteredMessagesTo\": \"\",\r\n              \"ForwardTo\": \"\",\r\n              \"LockDuration\": \"PT1M\",\r\n              \"MaxDeliveryCount\": 10,\r\n              \"RequiresDuplicateDetection\": false,\r\n              \"RequiresSession\": false\r\n            }\r\n          }\r\n        ],\r\n\r\n        \"Topics\": [\r\n          {\r\n            \"Name\": \"topic.1\",\r\n            \"Properties\": {\r\n              \"DefaultMessageTimeToLive\": \"PT1H\",\r\n              \"DuplicateDetectionHistoryTimeWindow\": \"PT20S\",\r\n              \"RequiresDuplicateDetection\": false\r\n            },\r\n            \"Subscriptions\": [\r\n              {\r\n                \"Name\": \"subscription.1\",\r\n                \"Properties\": {\r\n                  \"DeadLetteringOnMessageExpiration\": false,\r\n                  \"DefaultMessageTimeToLive\": \"PT1H\",\r\n                  \"LockDuration\": \"PT1M\",\r\n                  \"MaxDeliveryCount\": 10,\r\n                  \"ForwardDeadLetteredMessagesTo\": \"\",\r\n                  \"ForwardTo\": \"\",\r\n                  \"RequiresSession\": false\r\n                },\r\n                \"Rules\": [\r\n                  {\r\n                    \"Name\": \"app-prop-filter-1\",\r\n                    \"Properties\": {\r\n                      \"FilterType\": \"Correlation\",\r\n                      \"CorrelationFilter\": {\r\n                        \"ContentType\": \"application\/text\",\r\n                        \"CorrelationId\": \"id1\",\r\n                        \"Label\": \"subject1\",\r\n                        \"MessageId\": \"msgid1\",\r\n                        \"ReplyTo\": \"someQueue\",\r\n                        \"ReplyToSessionId\": \"sessionId\",\r\n                        \"SessionId\": \"session1\",\r\n                        \"To\": \"xyz\"\r\n                      }\r\n                    }\r\n                  }\r\n                ]\r\n              },\r\n              {\r\n                \"Name\": \"subscription.2\",\r\n                \"Properties\": {\r\n                  \"DeadLetteringOnMessageExpiration\": false,\r\n                  \"DefaultMessageTimeToLive\": \"PT1H\",\r\n                  \"LockDuration\": \"PT1M\",\r\n                  \"MaxDeliveryCount\": 10,\r\n                  \"ForwardDeadLetteredMessagesTo\": \"\",\r\n                  \"ForwardTo\": \"\",\r\n                  \"RequiresSession\": false\r\n                },\r\n                \"Rules\": [\r\n                  {\r\n                    \"Name\": \"user-prop-filter-1\",\r\n                    \"Properties\": {\r\n                      \"FilterType\": \"Correlation\",\r\n                      \"CorrelationFilter\": {\r\n                        \"Properties\": {\r\n                          \"prop3\": \"value3\"\r\n                        }\r\n                      }\r\n                    }\r\n                  }\r\n                ]\r\n              },\r\n              {\r\n                \"Name\": \"subscription.3\",\r\n                \"Properties\": {\r\n                  \"DeadLetteringOnMessageExpiration\": false,\r\n                  \"DefaultMessageTimeToLive\": \"PT1H\",\r\n                  \"LockDuration\": \"PT1M\",\r\n                  \"MaxDeliveryCount\": 10,\r\n                  \"ForwardDeadLetteredMessagesTo\": \"\",\r\n                  \"ForwardTo\": \"\",\r\n                  \"RequiresSession\": false\r\n                }\r\n              }\r\n            ]\r\n          }\r\n        ]\r\n      }\r\n    ],\r\n    \"Logging\": {\r\n      \"Type\": \"File\"\r\n    }\r\n  }\r\n}<\/code><\/pre>\n<ol start=\"4\">\n<li>Write a unit test that uses the Docker container:<\/li>\n<\/ol>\n<pre><code class=\"language-java\">import com.azure.messaging.servicebus.ServiceBusMessage;\r\nimport com.azure.messaging.servicebus.ServiceBusSenderClient;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusMessagingAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusConnectionDetails;\r\nimport com.azure.spring.cloud.service.servicebus.consumer.ServiceBusErrorHandler;\r\nimport com.azure.spring.cloud.service.servicebus.consumer.ServiceBusRecordMessageListener;\r\nimport com.azure.spring.messaging.servicebus.core.ServiceBusTemplate;\r\nimport org.junit.jupiter.api.Test;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.boot.autoconfigure.ImportAutoConfiguration;\r\nimport org.springframework.boot.test.context.SpringBootTest;\r\nimport org.springframework.context.annotation.Bean;\r\nimport org.springframework.context.annotation.Configuration;\r\nimport org.springframework.messaging.support.MessageBuilder;\r\n\r\nimport java.time.Duration;\r\nimport java.util.Set;\r\nimport java.util.concurrent.ConcurrentHashMap;\r\n\r\nimport static org.assertj.core.api.Assertions.assertThat;\r\nimport static org.awaitility.Awaitility.waitAtMost;\r\n\r\n@SpringBootTest(properties = {\r\n        \"spring.docker.compose.skip.in-tests=false\",\r\n        \"spring.docker.compose.file=classpath:servicebus-compose.yaml\",\r\n        \"spring.docker.compose.stop.command=down\",\r\n        \"spring.docker.compose.readiness.timeout=PT5M\",\r\n        \"spring.cloud.azure.servicebus.namespace=sbemulatorns\",\r\n        \"spring.cloud.azure.servicebus.entity-name=queue.1\",\r\n        \"spring.cloud.azure.servicebus.entity-type=queue\",\r\n        \"spring.cloud.azure.servicebus.producer.entity-name=queue.1\",\r\n        \"spring.cloud.azure.servicebus.producer.entity-type=queue\",\r\n        \"spring.cloud.azure.servicebus.processor.entity-name=queue.1\",\r\n        \"spring.cloud.azure.servicebus.processor.entity-type=queue\"\r\n})\r\nclass ServiceBusDockerComposeTest {\r\n\r\n    @Autowired\r\n    private AzureServiceBusConnectionDetails connectionDetails;\r\n\r\n    @Autowired\r\n    private ServiceBusSenderClient senderClient;\r\n\r\n    @Autowired\r\n    private ServiceBusTemplate serviceBusTemplate;\r\n\r\n    @Test\r\n    void connectionDetailsShouldBeProvidedByFactory() {\r\n        assertThat(connectionDetails).isNotNull();\r\n        assertThat(connectionDetails.getConnectionString())\r\n                .isNotBlank()\r\n                .startsWith(\"Endpoint=sb:\/\/\");\r\n    }\r\n\r\n    @Test\r\n    void senderClientCanSendMessage() {\r\n        \/\/ Wait for Service Bus emulator to be fully ready and queue entity to be available\r\n        \/\/ The emulator depends on SQL Edge and needs time to initialize the messaging entities\r\n        waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -&gt; {\r\n            this.senderClient.sendMessage(new ServiceBusMessage(\"Hello World!\"));\r\n        });\r\n\r\n        waitAtMost(Duration.ofSeconds(30)).pollDelay(Duration.ofSeconds(5)).untilAsserted(() -&gt; {\r\n            assertThat(Config.MESSAGES).contains(\"Hello World!\");\r\n        });\r\n    }\r\n\r\n    @Test\r\n    void serviceBusTemplateCanSendMessage() {\r\n        \/\/ Wait for Service Bus emulator to be fully ready and queue entity to be available\r\n        \/\/ The emulator depends on SQL Edge and needs time to initialize the messaging entities\r\n        waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -&gt; {\r\n            this.serviceBusTemplate.sendAsync(\"queue.1\", MessageBuilder.withPayload(\"Hello from ServiceBusTemplate!\").build()).block(Duration.ofSeconds(10));\r\n        });\r\n\r\n        waitAtMost(Duration.ofSeconds(30)).pollDelay(Duration.ofSeconds(5)).untilAsserted(() -&gt; {\r\n            assertThat(Config.MESSAGES).contains(\"Hello from ServiceBusTemplate!\");\r\n        });\r\n    }\r\n\r\n    @Configuration(proxyBeanMethods = false)\r\n    @ImportAutoConfiguration(classes = {\r\n            AzureGlobalPropertiesAutoConfiguration.class,\r\n            AzureServiceBusAutoConfiguration.class,\r\n            AzureServiceBusMessagingAutoConfiguration.class})\r\n    static class Config {\r\n\r\n        private static final Set&lt;String&gt; MESSAGES = ConcurrentHashMap.newKeySet();\r\n\r\n        @Bean\r\n        ServiceBusRecordMessageListener processMessage() {\r\n            return context -&gt; {\r\n                MESSAGES.add(context.getMessage().getBody().toString());\r\n            };\r\n        }\r\n\r\n        @Bean\r\n        ServiceBusErrorHandler errorHandler() {\r\n            \/\/ No-op error handler for tests: acknowledge errors without affecting test execution.\r\n            return (context) -&gt; {\r\n            };\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<h5>Using Testcontainers<\/h5>\n<ol>\n<li>Add the following dependency to your <code>pom.xml<\/code> file:<\/li>\n<\/ol>\n<pre><code class=\"language-xml\">  &lt;properties&gt;\r\n    &lt;version.spring.cloud.azure&gt;7.1.0&lt;\/version.spring.cloud.azure&gt;\r\n  &lt;\/properties&gt;\r\n\r\n  &lt;dependencyManagement&gt;\r\n    &lt;dependencies&gt;\r\n      &lt;dependency&gt;\r\n        &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;spring-cloud-azure-dependencies&lt;\/artifactId&gt;\r\n        &lt;version&gt;${version.spring.cloud.azure}&lt;\/version&gt;\r\n        &lt;type&gt;pom&lt;\/type&gt;\r\n        &lt;scope&gt;import&lt;\/scope&gt;\r\n      &lt;\/dependency&gt;\r\n    &lt;\/dependencies&gt;\r\n  &lt;\/dependencyManagement&gt;\r\n\r\n  &lt;dependencies&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-messaging-azure-servicebus&lt;\/artifactId&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-starter&lt;\/artifactId&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-boot-starter-test&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-boot-testcontainers&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;org.testcontainers&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;testcontainers-junit-jupiter&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-testcontainers&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.microsoft.sqlserver&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;mssql-jdbc&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n  &lt;\/dependencies&gt;<\/code><\/pre>\n<ol start=\"2\">\n<li>Create a <code>Config.json<\/code> file in the <code>src\/test\/resources<\/code> directory with the following content:<\/li>\n<\/ol>\n<pre><code class=\"language-json\">{\r\n  \"UserConfig\": {\r\n    \"Namespaces\": [\r\n      {\r\n        \"Name\": \"sbemulatorns\",\r\n        \"Queues\": [\r\n          {\r\n            \"Name\": \"queue.1\",\r\n            \"Properties\": {\r\n              \"DeadLetteringOnMessageExpiration\": false,\r\n              \"DefaultMessageTimeToLive\": \"PT1H\",\r\n              \"DuplicateDetectionHistoryTimeWindow\": \"PT20S\",\r\n              \"ForwardDeadLetteredMessagesTo\": \"\",\r\n              \"ForwardTo\": \"\",\r\n              \"LockDuration\": \"PT1M\",\r\n              \"MaxDeliveryCount\": 10,\r\n              \"RequiresDuplicateDetection\": false,\r\n              \"RequiresSession\": false\r\n            }\r\n          }\r\n        ],\r\n\r\n        \"Topics\": [\r\n          {\r\n            \"Name\": \"topic.1\",\r\n            \"Properties\": {\r\n              \"DefaultMessageTimeToLive\": \"PT1H\",\r\n              \"DuplicateDetectionHistoryTimeWindow\": \"PT20S\",\r\n              \"RequiresDuplicateDetection\": false\r\n            },\r\n            \"Subscriptions\": [\r\n              {\r\n                \"Name\": \"subscription.1\",\r\n                \"Properties\": {\r\n                  \"DeadLetteringOnMessageExpiration\": false,\r\n                  \"DefaultMessageTimeToLive\": \"PT1H\",\r\n                  \"LockDuration\": \"PT1M\",\r\n                  \"MaxDeliveryCount\": 10,\r\n                  \"ForwardDeadLetteredMessagesTo\": \"\",\r\n                  \"ForwardTo\": \"\",\r\n                  \"RequiresSession\": false\r\n                },\r\n                \"Rules\": [\r\n                  {\r\n                    \"Name\": \"app-prop-filter-1\",\r\n                    \"Properties\": {\r\n                      \"FilterType\": \"Correlation\",\r\n                      \"CorrelationFilter\": {\r\n                        \"ContentType\": \"application\/text\",\r\n                        \"CorrelationId\": \"id1\",\r\n                        \"Label\": \"subject1\",\r\n                        \"MessageId\": \"msgid1\",\r\n                        \"ReplyTo\": \"someQueue\",\r\n                        \"ReplyToSessionId\": \"sessionId\",\r\n                        \"SessionId\": \"session1\",\r\n                        \"To\": \"xyz\"\r\n                      }\r\n                    }\r\n                  }\r\n                ]\r\n              },\r\n              {\r\n                \"Name\": \"subscription.2\",\r\n                \"Properties\": {\r\n                  \"DeadLetteringOnMessageExpiration\": false,\r\n                  \"DefaultMessageTimeToLive\": \"PT1H\",\r\n                  \"LockDuration\": \"PT1M\",\r\n                  \"MaxDeliveryCount\": 10,\r\n                  \"ForwardDeadLetteredMessagesTo\": \"\",\r\n                  \"ForwardTo\": \"\",\r\n                  \"RequiresSession\": false\r\n                },\r\n                \"Rules\": [\r\n                  {\r\n                    \"Name\": \"user-prop-filter-1\",\r\n                    \"Properties\": {\r\n                      \"FilterType\": \"Correlation\",\r\n                      \"CorrelationFilter\": {\r\n                        \"Properties\": {\r\n                          \"prop3\": \"value3\"\r\n                        }\r\n                      }\r\n                    }\r\n                  }\r\n                ]\r\n              },\r\n              {\r\n                \"Name\": \"subscription.3\",\r\n                \"Properties\": {\r\n                  \"DeadLetteringOnMessageExpiration\": false,\r\n                  \"DefaultMessageTimeToLive\": \"PT1H\",\r\n                  \"LockDuration\": \"PT1M\",\r\n                  \"MaxDeliveryCount\": 10,\r\n                  \"ForwardDeadLetteredMessagesTo\": \"\",\r\n                  \"ForwardTo\": \"\",\r\n                  \"RequiresSession\": false\r\n                }\r\n              }\r\n            ]\r\n          }\r\n        ]\r\n      }\r\n    ],\r\n    \"Logging\": {\r\n      \"Type\": \"File\"\r\n    }\r\n  }\r\n}<\/code><\/pre>\n<ol start=\"3\">\n<li>Write a unit test that uses the Docker container:<\/li>\n<\/ol>\n<pre><code class=\"language-java\">import com.azure.messaging.servicebus.ServiceBusMessage;\r\nimport com.azure.messaging.servicebus.ServiceBusSenderClient;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusMessagingAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.servicebus.properties.AzureServiceBusConnectionDetails;\r\nimport com.azure.spring.cloud.service.servicebus.consumer.ServiceBusErrorHandler;\r\nimport com.azure.spring.cloud.service.servicebus.consumer.ServiceBusRecordMessageListener;\r\nimport com.azure.spring.messaging.servicebus.core.ServiceBusTemplate;\r\nimport org.junit.jupiter.api.Test;\r\nimport org.springframework.beans.factory.annotation.Autowired;\r\nimport org.springframework.boot.autoconfigure.ImportAutoConfiguration;\r\nimport org.springframework.boot.testcontainers.service.connection.ServiceConnection;\r\nimport org.springframework.context.annotation.Bean;\r\nimport org.springframework.context.annotation.Configuration;\r\nimport org.springframework.messaging.support.MessageBuilder;\r\nimport org.springframework.test.context.TestPropertySource;\r\nimport org.springframework.test.context.junit.jupiter.SpringJUnitConfig;\r\nimport org.testcontainers.azure.ServiceBusEmulatorContainer;\r\nimport org.testcontainers.containers.MSSQLServerContainer;\r\nimport org.testcontainers.containers.Network;\r\nimport org.testcontainers.junit.jupiter.Container;\r\nimport org.testcontainers.junit.jupiter.Testcontainers;\r\nimport org.testcontainers.utility.MountableFile;\r\n\r\nimport java.time.Duration;\r\nimport java.util.Set;\r\nimport java.util.concurrent.ConcurrentHashMap;\r\n\r\nimport static org.assertj.core.api.Assertions.assertThat;\r\nimport static org.awaitility.Awaitility.waitAtMost;\r\n\r\n@SpringJUnitConfig\r\n@TestPropertySource(properties = { \"spring.cloud.azure.servicebus.entity-name=queue.1\",\r\n        \"spring.cloud.azure.servicebus.entity-type=queue\" })\r\n@Testcontainers\r\nclass ServiceBusTestContainerTest {\r\n\r\n    private static final Network NETWORK = Network.newNetwork();\r\n\r\n    private static final MSSQLServerContainer&lt;?&gt; SQLSERVER = new MSSQLServerContainer&lt;&gt;(\r\n            \"mcr.microsoft.com\/mssql\/server:2022-CU14-ubuntu-22.04\")\r\n            .acceptLicense()\r\n            .withNetwork(NETWORK)\r\n            .withNetworkAliases(\"sqlserver\");\r\n\r\n    @Container\r\n    @ServiceConnection\r\n    private static final ServiceBusEmulatorContainer SERVICE_BUS = new ServiceBusEmulatorContainer(\r\n            \"mcr.microsoft.com\/azure-messaging\/servicebus-emulator:latest\")\r\n            .acceptLicense()\r\n            .withCopyFileToContainer(MountableFile.forClasspathResource(\"Config.json\"),\r\n                    \"\/ServiceBus_Emulator\/ConfigFiles\/Config.json\")\r\n            .withNetwork(NETWORK)\r\n            .withMsSqlServerContainer(SQLSERVER);\r\n\r\n    @Autowired\r\n    private AzureServiceBusConnectionDetails connectionDetails;\r\n\r\n    @Autowired\r\n    private ServiceBusSenderClient senderClient;\r\n\r\n    @Autowired\r\n    private ServiceBusTemplate serviceBusTemplate;\r\n\r\n    @Test\r\n    void connectionDetailsShouldBeProvidedByFactory() {\r\n        assertThat(connectionDetails).isNotNull();\r\n        assertThat(connectionDetails.getConnectionString())\r\n                .isNotBlank()\r\n                .startsWith(\"Endpoint=sb:\/\/\");\r\n    }\r\n\r\n    @Test\r\n    void senderClientCanSendMessage() {\r\n        \/\/ Wait for Service Bus emulator to be fully ready and queue entity to be available\r\n        waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -&gt; {\r\n            this.senderClient.sendMessage(new ServiceBusMessage(\"Hello World!\"));\r\n        });\r\n\r\n        waitAtMost(Duration.ofSeconds(30)).untilAsserted(() -&gt; {\r\n            assertThat(Config.MESSAGES).contains(\"Hello World!\");\r\n        });\r\n    }\r\n\r\n    @Test\r\n    void serviceBusTemplateCanSendMessage() {\r\n        \/\/ Wait for Service Bus emulator to be fully ready and queue entity to be available\r\n        waitAtMost(Duration.ofSeconds(120)).pollInterval(Duration.ofSeconds(2)).untilAsserted(() -&gt; {\r\n            this.serviceBusTemplate.sendAsync(\"queue.1\", MessageBuilder.withPayload(\"Hello from ServiceBusTemplate!\").build()).block(Duration.ofSeconds(10));\r\n        });\r\n\r\n        waitAtMost(Duration.ofSeconds(30)).untilAsserted(() -&gt; {\r\n            assertThat(Config.MESSAGES).contains(\"Hello from ServiceBusTemplate!\");\r\n        });\r\n    }\r\n\r\n    @Configuration(proxyBeanMethods = false)\r\n    @ImportAutoConfiguration(classes = {AzureGlobalPropertiesAutoConfiguration.class,\r\n            AzureServiceBusAutoConfiguration.class,\r\n            AzureServiceBusMessagingAutoConfiguration.class})\r\n    static class Config {\r\n\r\n        private static final Set&lt;String&gt; MESSAGES = ConcurrentHashMap.newKeySet();\r\n\r\n        @Bean\r\n        ServiceBusRecordMessageListener processMessage() {\r\n            return context -&gt; {\r\n                MESSAGES.add(context.getMessage().getBody().toString());\r\n            };\r\n        }\r\n\r\n        @Bean\r\n        ServiceBusErrorHandler errorHandler() {\r\n            \/\/ No-op error handler for tests: acknowledge errors without affecting test execution.\r\n            return (context) -&gt; {\r\n            };\r\n        }\r\n\r\n    }\r\n}<\/code><\/pre>\n<h4>Spring Cloud Stream binders with Service Bus<\/h4>\n<h5>Using Docker Compose<\/h5>\n<ol>\n<li>Add the following dependency to your <code>pom.xml<\/code> file:<\/li>\n<\/ol>\n<pre><code class=\"language-xml\">  &lt;properties&gt;\r\n    &lt;version.spring.cloud.azure&gt;7.1.0&lt;\/version.spring.cloud.azure&gt;\r\n  &lt;\/properties&gt;\r\n\r\n  &lt;dependencyManagement&gt;\r\n    &lt;dependencies&gt;\r\n      &lt;dependency&gt;\r\n        &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;spring-cloud-azure-dependencies&lt;\/artifactId&gt;\r\n        &lt;version&gt;${version.spring.cloud.azure}&lt;\/version&gt;\r\n        &lt;type&gt;pom&lt;\/type&gt;\r\n        &lt;scope&gt;import&lt;\/scope&gt;\r\n      &lt;\/dependency&gt;\r\n    &lt;\/dependencies&gt;\r\n  &lt;\/dependencyManagement&gt;\r\n\r\n  &lt;dependencies&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-stream-binder-servicebus&lt;\/artifactId&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-boot-starter-test&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-autoconfigure&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-docker-compose&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n  &lt;\/dependencies&gt;<\/code><\/pre>\n<ol start=\"2\">\n<li>Create a <code>servicebus-compose.yaml<\/code> file in the <code>src\/test\/resources<\/code> directory with the following content:<\/li>\n<\/ol>\n<pre><code class=\"language-yaml\">services:\r\n  servicebus:\r\n    image: mcr.microsoft.com\/azure-messaging\/servicebus-emulator:latest\r\n    pull_policy: always\r\n    volumes:\r\n      - \".\/Config.json:\/ServiceBus_Emulator\/ConfigFiles\/Config.json\"\r\n    ports:\r\n      - \"5672\"\r\n    environment:\r\n      SQL_SERVER: sqledge\r\n      MSSQL_SA_PASSWORD: A_Str0ng_Required_Password\r\n      ACCEPT_EULA: Y\r\n    depends_on:\r\n      - sqledge\r\n    networks:\r\n      sb-emulator:\r\n        aliases:\r\n          - \"sb-emulator\"\r\n  sqledge:\r\n    image: \"mcr.microsoft.com\/azure-sql-edge:latest\"\r\n    networks:\r\n      sb-emulator:\r\n        aliases:\r\n          - \"sqledge\"\r\n    environment:\r\n      ACCEPT_EULA: Y\r\n      MSSQL_SA_PASSWORD: A_Str0ng_Required_Password\r\n\r\nnetworks:\r\n  sb-emulator:<\/code><\/pre>\n<ol start=\"3\">\n<li>Create a <code>Config.json<\/code> file in the <code>src\/test\/resources<\/code> directory with the following content:<\/li>\n<\/ol>\n<pre><code class=\"language-json\">{\r\n  \"UserConfig\": {\r\n    \"Namespaces\": [\r\n      {\r\n        \"Name\": \"sbemulatorns\",\r\n        \"Queues\": [\r\n          {\r\n            \"Name\": \"queue.1\",\r\n            \"Properties\": {\r\n              \"DeadLetteringOnMessageExpiration\": false,\r\n              \"DefaultMessageTimeToLive\": \"PT1H\",\r\n              \"DuplicateDetectionHistoryTimeWindow\": \"PT20S\",\r\n              \"ForwardDeadLetteredMessagesTo\": \"\",\r\n              \"ForwardTo\": \"\",\r\n              \"LockDuration\": \"PT1M\",\r\n              \"MaxDeliveryCount\": 10,\r\n              \"RequiresDuplicateDetection\": false,\r\n              \"RequiresSession\": false\r\n            }\r\n          }\r\n        ],\r\n\r\n        \"Topics\": [\r\n          {\r\n            \"Name\": \"topic.1\",\r\n            \"Properties\": {\r\n              \"DefaultMessageTimeToLive\": \"PT1H\",\r\n              \"DuplicateDetectionHistoryTimeWindow\": \"PT20S\",\r\n              \"RequiresDuplicateDetection\": false\r\n            },\r\n            \"Subscriptions\": [\r\n              {\r\n                \"Name\": \"subscription.1\",\r\n                \"Properties\": {\r\n                  \"DeadLetteringOnMessageExpiration\": false,\r\n                  \"DefaultMessageTimeToLive\": \"PT1H\",\r\n                  \"LockDuration\": \"PT1M\",\r\n                  \"MaxDeliveryCount\": 10,\r\n                  \"ForwardDeadLetteredMessagesTo\": \"\",\r\n                  \"ForwardTo\": \"\",\r\n                  \"RequiresSession\": false\r\n                },\r\n                \"Rules\": [\r\n                  {\r\n                    \"Name\": \"app-prop-filter-1\",\r\n                    \"Properties\": {\r\n                      \"FilterType\": \"Correlation\",\r\n                      \"CorrelationFilter\": {\r\n                        \"ContentType\": \"application\/text\",\r\n                        \"CorrelationId\": \"id1\",\r\n                        \"Label\": \"subject1\",\r\n                        \"MessageId\": \"msgid1\",\r\n                        \"ReplyTo\": \"someQueue\",\r\n                        \"ReplyToSessionId\": \"sessionId\",\r\n                        \"SessionId\": \"session1\",\r\n                        \"To\": \"xyz\"\r\n                      }\r\n                    }\r\n                  }\r\n                ]\r\n              },\r\n              {\r\n                \"Name\": \"subscription.2\",\r\n                \"Properties\": {\r\n                  \"DeadLetteringOnMessageExpiration\": false,\r\n                  \"DefaultMessageTimeToLive\": \"PT1H\",\r\n                  \"LockDuration\": \"PT1M\",\r\n                  \"MaxDeliveryCount\": 10,\r\n                  \"ForwardDeadLetteredMessagesTo\": \"\",\r\n                  \"ForwardTo\": \"\",\r\n                  \"RequiresSession\": false\r\n                },\r\n                \"Rules\": [\r\n                  {\r\n                    \"Name\": \"user-prop-filter-1\",\r\n                    \"Properties\": {\r\n                      \"FilterType\": \"Correlation\",\r\n                      \"CorrelationFilter\": {\r\n                        \"Properties\": {\r\n                          \"prop3\": \"value3\"\r\n                        }\r\n                      }\r\n                    }\r\n                  }\r\n                ]\r\n              },\r\n              {\r\n                \"Name\": \"subscription.3\",\r\n                \"Properties\": {\r\n                  \"DeadLetteringOnMessageExpiration\": false,\r\n                  \"DefaultMessageTimeToLive\": \"PT1H\",\r\n                  \"LockDuration\": \"PT1M\",\r\n                  \"MaxDeliveryCount\": 10,\r\n                  \"ForwardDeadLetteredMessagesTo\": \"\",\r\n                  \"ForwardTo\": \"\",\r\n                  \"RequiresSession\": false\r\n                }\r\n              }\r\n            ]\r\n          }\r\n        ]\r\n      }\r\n    ],\r\n    \"Logging\": {\r\n      \"Type\": \"File\"\r\n    }\r\n  }\r\n}<\/code><\/pre>\n<ol start=\"4\">\n<li>Write a unit test that uses the Docker container:<\/li>\n<\/ol>\n<pre><code class=\"language-java\">import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusMessagingAutoConfiguration;\r\nimport com.azure.spring.messaging.checkpoint.Checkpointer;\r\nimport org.junit.jupiter.api.Test;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\r\nimport org.springframework.boot.autoconfigure.ImportAutoConfiguration;\r\nimport org.springframework.boot.test.context.SpringBootTest;\r\nimport org.springframework.context.annotation.Bean;\r\nimport org.springframework.context.annotation.Configuration;\r\nimport org.springframework.messaging.Message;\r\nimport org.springframework.messaging.support.MessageBuilder;\r\n\r\nimport java.time.Duration;\r\nimport java.util.Set;\r\nimport java.util.concurrent.ConcurrentHashMap;\r\nimport java.util.concurrent.atomic.AtomicInteger;\r\nimport java.util.function.Consumer;\r\nimport java.util.function.Supplier;\r\n\r\nimport static com.azure.spring.messaging.AzureHeaders.CHECKPOINTER;\r\nimport static org.assertj.core.api.Assertions.assertThat;\r\nimport static org.awaitility.Awaitility.waitAtMost;\r\n\r\n@SpringBootTest(properties = {\r\n        \"spring.docker.compose.skip.in-tests=false\",\r\n        \"spring.docker.compose.file=classpath:servicebus-compose.yaml\",\r\n        \"spring.docker.compose.stop.command=down\",\r\n        \"spring.cloud.function.definition=consume;supply\",\r\n        \"spring.cloud.stream.bindings.consume-in-0.destination=queue.1\",\r\n        \"spring.cloud.stream.bindings.supply-out-0.destination=queue.1\",\r\n        \"spring.cloud.stream.servicebus.bindings.consume-in-0.consumer.auto-complete=false\",\r\n        \"spring.cloud.stream.servicebus.bindings.supply-out-0.producer.entity-type=queue\",\r\n        \"spring.cloud.stream.poller.fixed-delay=1000\",\r\n        \"spring.cloud.stream.poller.initial-delay=0\"\r\n})\r\nclass ServiceBusDockerComposeTest {\r\n\r\n    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceBusDockerComposeTest.class);\r\n    private static final Set&lt;String&gt; RECEIVED_MESSAGES = ConcurrentHashMap.newKeySet();\r\n    private static final AtomicInteger MESSAGE_SEQUENCE = new AtomicInteger(0);\r\n\r\n    @Test\r\n    void supplierAndConsumerShouldWorkThroughServiceBusQueue() {\r\n        waitAtMost(Duration.ofSeconds(60))\r\n            .pollDelay(Duration.ofSeconds(2))\r\n            .untilAsserted(() -&gt; {\r\n                assertThat(RECEIVED_MESSAGES).isNotEmpty();\r\n                LOGGER.info(\"\u2713 Test passed - Consumer received {} message(s)\", RECEIVED_MESSAGES.size());\r\n            });\r\n    }\r\n\r\n    @Configuration(proxyBeanMethods = false)\r\n    @EnableAutoConfiguration\r\n    @ImportAutoConfiguration(classes = {\r\n            AzureGlobalPropertiesAutoConfiguration.class,\r\n            AzureServiceBusAutoConfiguration.class,\r\n            AzureServiceBusMessagingAutoConfiguration.class})\r\n    static class Config {\r\n\r\n        @Bean\r\n        public Supplier&lt;Message&lt;String&gt;&gt; supply() {\r\n            return () -&gt; {\r\n                int sequence = MESSAGE_SEQUENCE.getAndIncrement();\r\n                String payload = \"Hello world, \" + sequence;\r\n                LOGGER.info(\"[Supplier] Invoked - message sequence: {}\", sequence);\r\n                return MessageBuilder.withPayload(payload).build();\r\n            };\r\n        }\r\n\r\n        @Bean\r\n        public Consumer&lt;Message&lt;String&gt;&gt; consume() {\r\n            return message -&gt; {\r\n                String payload = message.getPayload();\r\n                RECEIVED_MESSAGES.add(payload);\r\n                LOGGER.info(\"[Consumer] Received message: {}\", payload);\r\n\r\n                Checkpointer checkpointer = (Checkpointer) message.getHeaders().get(CHECKPOINTER);\r\n                if (checkpointer != null) {\r\n                    checkpointer.success()\r\n                            .doOnSuccess(s -&gt; LOGGER.info(\"[Consumer] Message checkpointed\"))\r\n                            .doOnError(e -&gt; LOGGER.error(\"[Consumer] Checkpoint failed\", e))\r\n                            .block();\r\n                }\r\n            };\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<h5>Using Testcontainers<\/h5>\n<ol>\n<li>Add the following dependency to your <code>pom.xml<\/code> file:<\/li>\n<\/ol>\n<pre><code class=\"language-xml\">  &lt;properties&gt;\r\n    &lt;version.spring.cloud.azure&gt;7.1.0&lt;\/version.spring.cloud.azure&gt;\r\n  &lt;\/properties&gt;\r\n\r\n  &lt;dependencyManagement&gt;\r\n    &lt;dependencies&gt;\r\n      &lt;dependency&gt;\r\n        &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n        &lt;artifactId&gt;spring-cloud-azure-dependencies&lt;\/artifactId&gt;\r\n        &lt;version&gt;${version.spring.cloud.azure}&lt;\/version&gt;\r\n        &lt;type&gt;pom&lt;\/type&gt;\r\n        &lt;scope&gt;import&lt;\/scope&gt;\r\n      &lt;\/dependency&gt;\r\n    &lt;\/dependencies&gt;\r\n  &lt;\/dependencyManagement&gt;\r\n\r\n  &lt;dependencies&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-stream-binder-servicebus&lt;\/artifactId&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-starter&lt;\/artifactId&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-boot-starter-test&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-boot-testcontainers&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;org.testcontainers&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;testcontainers-junit-jupiter&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.azure.spring&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;spring-cloud-azure-testcontainers&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n    &lt;dependency&gt;\r\n      &lt;groupId&gt;com.microsoft.sqlserver&lt;\/groupId&gt;\r\n      &lt;artifactId&gt;mssql-jdbc&lt;\/artifactId&gt;\r\n      &lt;scope&gt;test&lt;\/scope&gt;\r\n    &lt;\/dependency&gt;\r\n  &lt;\/dependencies&gt;<\/code><\/pre>\n<ol start=\"2\">\n<li>Create a <code>Config.json<\/code> file in the <code>src\/test\/resources<\/code> directory with the following content:<\/li>\n<\/ol>\n<pre><code class=\"language-json\">{\r\n  \"UserConfig\": {\r\n    \"Namespaces\": [\r\n      {\r\n        \"Name\": \"sbemulatorns\",\r\n        \"Queues\": [\r\n          {\r\n            \"Name\": \"queue.1\",\r\n            \"Properties\": {\r\n              \"DeadLetteringOnMessageExpiration\": false,\r\n              \"DefaultMessageTimeToLive\": \"PT1H\",\r\n              \"DuplicateDetectionHistoryTimeWindow\": \"PT20S\",\r\n              \"ForwardDeadLetteredMessagesTo\": \"\",\r\n              \"ForwardTo\": \"\",\r\n              \"LockDuration\": \"PT1M\",\r\n              \"MaxDeliveryCount\": 10,\r\n              \"RequiresDuplicateDetection\": false,\r\n              \"RequiresSession\": false\r\n            }\r\n          }\r\n        ],\r\n\r\n        \"Topics\": [\r\n          {\r\n            \"Name\": \"topic.1\",\r\n            \"Properties\": {\r\n              \"DefaultMessageTimeToLive\": \"PT1H\",\r\n              \"DuplicateDetectionHistoryTimeWindow\": \"PT20S\",\r\n              \"RequiresDuplicateDetection\": false\r\n            },\r\n            \"Subscriptions\": [\r\n              {\r\n                \"Name\": \"subscription.1\",\r\n                \"Properties\": {\r\n                  \"DeadLetteringOnMessageExpiration\": false,\r\n                  \"DefaultMessageTimeToLive\": \"PT1H\",\r\n                  \"LockDuration\": \"PT1M\",\r\n                  \"MaxDeliveryCount\": 10,\r\n                  \"ForwardDeadLetteredMessagesTo\": \"\",\r\n                  \"ForwardTo\": \"\",\r\n                  \"RequiresSession\": false\r\n                },\r\n                \"Rules\": [\r\n                  {\r\n                    \"Name\": \"app-prop-filter-1\",\r\n                    \"Properties\": {\r\n                      \"FilterType\": \"Correlation\",\r\n                      \"CorrelationFilter\": {\r\n                        \"ContentType\": \"application\/text\",\r\n                        \"CorrelationId\": \"id1\",\r\n                        \"Label\": \"subject1\",\r\n                        \"MessageId\": \"msgid1\",\r\n                        \"ReplyTo\": \"someQueue\",\r\n                        \"ReplyToSessionId\": \"sessionId\",\r\n                        \"SessionId\": \"session1\",\r\n                        \"To\": \"xyz\"\r\n                      }\r\n                    }\r\n                  }\r\n                ]\r\n              },\r\n              {\r\n                \"Name\": \"subscription.2\",\r\n                \"Properties\": {\r\n                  \"DeadLetteringOnMessageExpiration\": false,\r\n                  \"DefaultMessageTimeToLive\": \"PT1H\",\r\n                  \"LockDuration\": \"PT1M\",\r\n                  \"MaxDeliveryCount\": 10,\r\n                  \"ForwardDeadLetteredMessagesTo\": \"\",\r\n                  \"ForwardTo\": \"\",\r\n                  \"RequiresSession\": false\r\n                },\r\n                \"Rules\": [\r\n                  {\r\n                    \"Name\": \"user-prop-filter-1\",\r\n                    \"Properties\": {\r\n                      \"FilterType\": \"Correlation\",\r\n                      \"CorrelationFilter\": {\r\n                        \"Properties\": {\r\n                          \"prop3\": \"value3\"\r\n                        }\r\n                      }\r\n                    }\r\n                  }\r\n                ]\r\n              },\r\n              {\r\n                \"Name\": \"subscription.3\",\r\n                \"Properties\": {\r\n                  \"DeadLetteringOnMessageExpiration\": false,\r\n                  \"DefaultMessageTimeToLive\": \"PT1H\",\r\n                  \"LockDuration\": \"PT1M\",\r\n                  \"MaxDeliveryCount\": 10,\r\n                  \"ForwardDeadLetteredMessagesTo\": \"\",\r\n                  \"ForwardTo\": \"\",\r\n                  \"RequiresSession\": false\r\n                }\r\n              }\r\n            ]\r\n          }\r\n        ]\r\n      }\r\n    ],\r\n    \"Logging\": {\r\n      \"Type\": \"File\"\r\n    }\r\n  }\r\n}<\/code><\/pre>\n<ol start=\"3\">\n<li>Write a unit test that uses the Docker container:<\/li>\n<\/ol>\n<pre><code class=\"language-java\">import com.azure.spring.cloud.autoconfigure.implementation.context.AzureGlobalPropertiesAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusAutoConfiguration;\r\nimport com.azure.spring.cloud.autoconfigure.implementation.servicebus.AzureServiceBusMessagingAutoConfiguration;\r\nimport com.azure.spring.messaging.checkpoint.Checkpointer;\r\nimport org.junit.jupiter.api.Test;\r\nimport org.slf4j.Logger;\r\nimport org.slf4j.LoggerFactory;\r\nimport org.springframework.boot.autoconfigure.EnableAutoConfiguration;\r\nimport org.springframework.boot.autoconfigure.ImportAutoConfiguration;\r\nimport org.springframework.boot.testcontainers.service.connection.ServiceConnection;\r\nimport org.springframework.context.annotation.Bean;\r\nimport org.springframework.context.annotation.Configuration;\r\nimport org.springframework.messaging.Message;\r\nimport org.springframework.messaging.support.MessageBuilder;\r\nimport org.springframework.test.context.TestPropertySource;\r\nimport org.springframework.test.context.junit.jupiter.SpringJUnitConfig;\r\nimport org.testcontainers.azure.ServiceBusEmulatorContainer;\r\nimport org.testcontainers.containers.MSSQLServerContainer;\r\nimport org.testcontainers.containers.Network;\r\nimport org.testcontainers.junit.jupiter.Container;\r\nimport org.testcontainers.junit.jupiter.Testcontainers;\r\nimport org.testcontainers.utility.MountableFile;\r\n\r\nimport java.time.Duration;\r\nimport java.util.Set;\r\nimport java.util.concurrent.ConcurrentHashMap;\r\nimport java.util.concurrent.atomic.AtomicInteger;\r\nimport java.util.function.Consumer;\r\nimport java.util.function.Supplier;\r\n\r\nimport static com.azure.spring.messaging.AzureHeaders.CHECKPOINTER;\r\nimport static org.assertj.core.api.Assertions.assertThat;\r\nimport static org.awaitility.Awaitility.waitAtMost;\r\n\r\n@SpringJUnitConfig\r\n@TestPropertySource(properties = {\r\n        \"spring.cloud.function.definition=consume;supply\",\r\n        \"spring.cloud.stream.bindings.consume-in-0.destination=queue.1\",\r\n        \"spring.cloud.stream.bindings.supply-out-0.destination=queue.1\",\r\n        \"spring.cloud.stream.servicebus.bindings.consume-in-0.consumer.auto-complete=false\",\r\n        \"spring.cloud.stream.servicebus.bindings.supply-out-0.producer.entity-type=queue\",\r\n        \"spring.cloud.stream.poller.fixed-delay=1000\",\r\n        \"spring.cloud.stream.poller.initial-delay=0\"})\r\n@Testcontainers\r\nclass ServiceBusTestContainerTest {\r\n\r\n    private static final Network NETWORK = Network.newNetwork();\r\n\r\n    private static final MSSQLServerContainer&lt;?&gt; SQLSERVER = new MSSQLServerContainer&lt;&gt;(\r\n            \"mcr.microsoft.com\/mssql\/server:2022-CU14-ubuntu-22.04\")\r\n            .acceptLicense()\r\n            .withNetwork(NETWORK)\r\n            .withNetworkAliases(\"sqlserver\");\r\n\r\n    @Container\r\n    @ServiceConnection\r\n    private static final ServiceBusEmulatorContainer SERVICE_BUS = new ServiceBusEmulatorContainer(\r\n            \"mcr.microsoft.com\/azure-messaging\/servicebus-emulator:latest\")\r\n            .acceptLicense()\r\n            .withCopyFileToContainer(MountableFile.forClasspathResource(\"Config.json\"),\r\n                    \"\/ServiceBus_Emulator\/ConfigFiles\/Config.json\")\r\n            .withNetwork(NETWORK)\r\n            .withMsSqlServerContainer(SQLSERVER);\r\n\r\n    private static final Logger LOGGER = LoggerFactory.getLogger(ServiceBusTestContainerTest.class);\r\n    private static final Set&lt;String&gt; RECEIVED_MESSAGES = ConcurrentHashMap.newKeySet();\r\n    private static final AtomicInteger MESSAGE_SEQUENCE = new AtomicInteger(0);\r\n\r\n    @Test\r\n    void supplierAndConsumerShouldWorkThroughServiceBusQueue() {\r\n        waitAtMost(Duration.ofSeconds(60))\r\n                .pollDelay(Duration.ofSeconds(2))\r\n                .untilAsserted(() -&gt; {\r\n                    assertThat(RECEIVED_MESSAGES).isNotEmpty();\r\n                    LOGGER.info(\"\u2713 Test passed - Consumer received {} message(s)\", RECEIVED_MESSAGES.size());\r\n                });\r\n    }\r\n\r\n    @Configuration(proxyBeanMethods = false)\r\n    @EnableAutoConfiguration\r\n    @ImportAutoConfiguration(classes = {\r\n            AzureGlobalPropertiesAutoConfiguration.class,\r\n            AzureServiceBusAutoConfiguration.class,\r\n            AzureServiceBusMessagingAutoConfiguration.class})\r\n    static class Config {\r\n\r\n        @Bean\r\n        public Supplier&lt;Message&lt;String&gt;&gt; supply() {\r\n            return () -&gt; {\r\n                int sequence = MESSAGE_SEQUENCE.getAndIncrement();\r\n                String payload = \"Hello world, \" + sequence;\r\n                LOGGER.info(\"[Supplier] Invoked - message sequence: {}\", sequence);\r\n                return MessageBuilder.withPayload(payload).build();\r\n            };\r\n        }\r\n\r\n        @Bean\r\n        public Consumer&lt;Message&lt;String&gt;&gt; consume() {\r\n            return message -&gt; {\r\n                String payload = message.getPayload();\r\n                RECEIVED_MESSAGES.add(payload);\r\n                LOGGER.info(\"[Consumer] Received message: {}\", payload);\r\n\r\n                Checkpointer checkpointer = (Checkpointer) message.getHeaders().get(CHECKPOINTER);\r\n                if (checkpointer != null) {\r\n                    checkpointer.success()\r\n                            .doOnSuccess(s -&gt; LOGGER.info(\"[Consumer] Message checkpointed\"))\r\n                            .doOnError(e -&gt; LOGGER.error(\"[Consumer] Checkpoint failed\", e))\r\n                            .block();\r\n                }\r\n            };\r\n        }\r\n    }\r\n}<\/code><\/pre>\n<h3>More examples<\/h3>\n<p>You can find more examples of using Docker for unit testing in Spring Cloud Azure at the following links:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/Azure-Samples\/azure-spring-boot-samples\/tree\/main\/spring-cloud-azure-docker-compose\">Docker Compose examples<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/Azure-Samples\/azure-spring-boot-samples\/tree\/main\/spring-cloud-azure-testcontainers\">Testcontainers examples<\/a><\/li>\n<\/ul>\n<h2>Summary<\/h2>\n<p>In this blog post, we explored how to write unit tests with Docker for Spring Cloud Azure applications. We saw examples of using Docker Compose and Testcontainers to start Docker containers for testing Azure services locally. This approach lets developers quickly verify code changes without creating real Azure resources and ensures that applications work as expected before deployment.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post shows how to write Azure service-related unit tests with Docker using Spring Cloud Azure.<\/p>\n","protected":false},"author":182048,"featured_media":3639,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[939,160,871,872,958],"class_list":["post-3636","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-azure-sdk","tag-docker","tag-java","tag-spring","tag-spring-cloud-azure","tag-unit-test"],"acf":[],"blog_post_summary":"<p>This post shows how to write Azure service-related unit tests with Docker using Spring Cloud Azure.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts\/3636","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/users\/182048"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/comments?post=3636"}],"version-history":[{"count":2,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts\/3636\/revisions"}],"predecessor-version":[{"id":3662,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/posts\/3636\/revisions\/3662"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/media\/3639"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/media?parent=3636"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/categories?post=3636"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/azure-sdk\/wp-json\/wp\/v2\/tags?post=3636"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}