diff --git a/docs/modules/azure.md b/docs/modules/azure.md
index 19c141c7639..e09634b09c4 100644
--- a/docs/modules/azure.md
+++ b/docs/modules/azure.md
@@ -5,11 +5,12 @@ This module is INCUBATING. While it is ready for use and operational in the curr
Testcontainers module for the Microsoft Azure's [SDK](https://github.com/Azure/azure-sdk-for-java).
-Currently, the module supports `Azurite` and `CosmosDB` emulators. In order to use them, you should use the following classes:
+Currently, the module supports `Azurite`, `Azure Event Hubs` and `CosmosDB` emulators. In order to use them, you should use the following classes:
Class | Container Image
-|-
AzuriteContainer | [mcr.microsoft.com/azure-storage/azurite](https://github.com/microsoft/containerregistry)
+AzureEventHubsContainer | [mcr.microsoft.com/azure-messaging/eventhubs-emulator](https://github.com/microsoft/containerregistry)
CosmosDBEmulatorContainer | [mcr.microsoft.com/cosmosdb/linux/azure-cosmos-emulator](https://github.com/microsoft/containerregistry)
## Usage example
@@ -72,6 +73,34 @@ Build Azure Table client:
!!! note
We can use custom credentials the same way as defined in the Blob section.
+### Azure Event Hubs Emulator
+
+
+[Configuring the Azure Event Hubs Emulator container](../../modules/azure/src/test/resources/eventhubs_config.json)
+
+
+Start Azure Event Hubs Emulator during a test:
+
+
+[Setting up a network](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:network
+
+
+
+[Starting an Azurite container as dependency](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:azuriteContainer
+
+
+
+[Starting an Azure Event Hubs Emulator container](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:emulatorContainer
+
+
+#### Using Azure Event Hubs clients
+
+Configure the consumer and the producer clients:
+
+
+[Configuring the clients](../../modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java) inside_block:createProducerAndConsumer
+
+
### CosmosDB
Start Azure CosmosDB Emulator during a test:
diff --git a/modules/azure/build.gradle b/modules/azure/build.gradle
index c6cfb6738d0..3dc97d03fce 100644
--- a/modules/azure/build.gradle
+++ b/modules/azure/build.gradle
@@ -10,4 +10,5 @@ dependencies {
testImplementation 'com.azure:azure-storage-blob:12.29.0'
testImplementation 'com.azure:azure-storage-queue:12.24.0'
testImplementation 'com.azure:azure-data-tables:12.5.0'
+ testImplementation 'com.azure:azure-messaging-eventhubs:5.19.2'
}
diff --git a/modules/azure/src/main/java/org/testcontainers/azure/AzureEventHubsContainer.java b/modules/azure/src/main/java/org/testcontainers/azure/AzureEventHubsContainer.java
new file mode 100644
index 00000000000..d611a2442cc
--- /dev/null
+++ b/modules/azure/src/main/java/org/testcontainers/azure/AzureEventHubsContainer.java
@@ -0,0 +1,109 @@
+package org.testcontainers.azure;
+
+import org.testcontainers.containers.GenericContainer;
+import org.testcontainers.containers.wait.strategy.Wait;
+import org.testcontainers.images.builder.Transferable;
+import org.testcontainers.utility.DockerImageName;
+import org.testcontainers.utility.LicenseAcceptance;
+
+/**
+ * Testcontainers implementation for Azure Eventhubs Emulator.
+ *
+ * Supported image: {@code "mcr.microsoft.com/azure-messaging/eventhubs-emulator"}
+ *
+ * Exposed ports:
+ *
+ */
+public class AzureEventHubsContainer extends GenericContainer {
+
+ private static final int DEFAULT_AMQP_PORT = 5672;
+
+ private static final String CONNECTION_STRING_FORMAT =
+ "Endpoint=sb://%s:%d;SharedAccessKeyName=RootManageSharedAccessKey;SharedAccessKey=SAS_KEY_VALUE;UseDevelopmentEmulator=true;";
+
+ private static final DockerImageName DEFAULT_IMAGE_NAME = DockerImageName.parse(
+ "mcr.microsoft.com/azure-messaging/eventhubs-emulator"
+ );
+
+ private AzuriteContainer azuriteContainer;
+
+ /**
+ * @param dockerImageName specified docker image name to run
+ */
+ public AzureEventHubsContainer(final String dockerImageName) {
+ this(DockerImageName.parse(dockerImageName));
+ }
+
+ /**
+ * @param dockerImageName specified docker image name to run
+ */
+ public AzureEventHubsContainer(final DockerImageName dockerImageName) {
+ super(dockerImageName);
+ dockerImageName.assertCompatibleWith(DEFAULT_IMAGE_NAME);
+ waitingFor(Wait.forLogMessage(".*Emulator Service is Successfully Up!.*", 1));
+ withExposedPorts(DEFAULT_AMQP_PORT);
+ }
+
+ /**
+ * * Sets the Azurite dependency needed by the Event Hubs Container,
+ *
+ * @param azuriteContainer The Azurite container used by Event HUbs as a dependency
+ * @return this
+ */
+ public AzureEventHubsContainer withAzuriteContainer(final AzuriteContainer azuriteContainer) {
+ this.azuriteContainer = azuriteContainer;
+ dependsOn(this.azuriteContainer);
+ return this;
+ }
+
+ /**
+ * Provide the broker configuration to the container.
+ *
+ * @param config The file containing the broker configuration
+ * @return this
+ */
+ public AzureEventHubsContainer withConfig(final Transferable config) {
+ withCopyToContainer(config, "/Eventhubs_Emulator/ConfigFiles/Config.json");
+ return this;
+ }
+
+ /**
+ * Accepts the EULA of the container.
+ *
+ * @return this
+ */
+ public AzureEventHubsContainer acceptLicense() {
+ withEnv("ACCEPT_EULA", "Y");
+ return this;
+ }
+
+ @Override
+ protected void configure() {
+ if (azuriteContainer == null) {
+ throw new IllegalStateException(
+ "The image " +
+ getDockerImageName() +
+ " requires an Azurite container. Please provide one with the withAzuriteContainer method!"
+ );
+ }
+ final String azuriteHost = azuriteContainer.getNetworkAliases().get(0);
+ withEnv("BLOB_SERVER", azuriteHost);
+ withEnv("METADATA_SERVER", azuriteHost);
+ // If license was not accepted programmatically, check if it was accepted via resource file
+ if (!getEnvMap().containsKey("ACCEPT_EULA")) {
+ LicenseAcceptance.assertLicenseAccepted(this.getDockerImageName());
+ acceptLicense();
+ }
+ }
+
+ /**
+ * Returns the connection string.
+ *
+ * @return connection string
+ */
+ public String getConnectionString() {
+ return String.format(CONNECTION_STRING_FORMAT, getHost(), getMappedPort(DEFAULT_AMQP_PORT));
+ }
+}
diff --git a/modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java b/modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java
new file mode 100644
index 00000000000..c0febc2ed86
--- /dev/null
+++ b/modules/azure/src/test/java/org/testcontainers/azure/AzureEventHubsContainerTest.java
@@ -0,0 +1,83 @@
+package org.testcontainers.azure;
+
+import com.azure.core.util.IterableStream;
+import com.azure.messaging.eventhubs.EventData;
+import com.azure.messaging.eventhubs.EventHubClientBuilder;
+import com.azure.messaging.eventhubs.EventHubConsumerClient;
+import com.azure.messaging.eventhubs.EventHubProducerClient;
+import com.azure.messaging.eventhubs.models.EventPosition;
+import com.azure.messaging.eventhubs.models.PartitionEvent;
+import org.junit.Rule;
+import org.junit.Test;
+import org.testcontainers.containers.Network;
+import org.testcontainers.utility.MountableFile;
+
+import java.time.Duration;
+import java.util.Collections;
+import java.util.Optional;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.waitAtMost;
+
+public class AzureEventHubsContainerTest {
+
+ @Rule
+ // network {
+ public Network network = Network.newNetwork();
+
+ // }
+
+ @Rule
+ // azuriteContainer {
+ public AzuriteContainer azuriteContainer = new AzuriteContainer("mcr.microsoft.com/azure-storage/azurite:3.33.0")
+ .withNetwork(network);
+
+ // }
+
+ @Rule
+ // emulatorContainer {
+ public AzureEventHubsContainer emulator = new AzureEventHubsContainer(
+ "mcr.microsoft.com/azure-messaging/eventhubs-emulator:2.0.1"
+ )
+ .acceptLicense()
+ .withNetwork(network)
+ .withConfig(MountableFile.forClasspathResource("/eventhubs_config.json"))
+ .withAzuriteContainer(azuriteContainer);
+
+ // }
+
+ @Test
+ public void testWithEventHubsClient() {
+ try (
+ // createProducerAndConsumer {
+ EventHubProducerClient producer = new EventHubClientBuilder()
+ .connectionString(emulator.getConnectionString())
+ .fullyQualifiedNamespace("emulatorNs1")
+ .eventHubName("eh1")
+ .buildProducerClient();
+ EventHubConsumerClient consumer = new EventHubClientBuilder()
+ .connectionString(emulator.getConnectionString())
+ .fullyQualifiedNamespace("emulatorNs1")
+ .eventHubName("eh1")
+ .consumerGroup("cg1")
+ .buildConsumerClient()
+ // }
+ ) {
+ producer.send(Collections.singletonList(new EventData("test")));
+
+ waitAtMost(Duration.ofSeconds(30))
+ .pollDelay(Duration.ofSeconds(5))
+ .untilAsserted(() -> {
+ IterableStream events = consumer.receiveFromPartition(
+ "0",
+ 1,
+ EventPosition.earliest(),
+ Duration.ofSeconds(2)
+ );
+ Optional event = events.stream().findFirst();
+ assertThat(event).isPresent();
+ assertThat(event.get().getData().getBodyAsString()).isEqualTo("test");
+ });
+ }
+ }
+}
diff --git a/modules/azure/src/test/resources/eventhubs_config.json b/modules/azure/src/test/resources/eventhubs_config.json
new file mode 100644
index 00000000000..554be9d7cbf
--- /dev/null
+++ b/modules/azure/src/test/resources/eventhubs_config.json
@@ -0,0 +1,24 @@
+{
+ "UserConfig": {
+ "NamespaceConfig": [
+ {
+ "Type": "EventHub",
+ "Name": "emulatorNs1",
+ "Entities": [
+ {
+ "Name": "eh1",
+ "PartitionCount": "1",
+ "ConsumerGroups": [
+ {
+ "Name": "cg1"
+ }
+ ]
+ }
+ ]
+ }
+ ],
+ "LoggingConfig": {
+ "Type": "File"
+ }
+ }
+}