From 1d21b9b8b93c2d505d6e87e7f79d0258cbc4bcf9 Mon Sep 17 00:00:00 2001 From: Artur Ciocanu Date: Tue, 23 Dec 2025 23:34:37 -0800 Subject: [PATCH 1/4] Adding DaprSpringBootTest and DaprSidecarContainer annotation for easier IT authoring. Signed-off-by: Artur Ciocanu --- .../testcontainers/actors/DaprActorsIT.java | 55 ++------- testcontainers-dapr/pom.xml | 52 +++++++-- .../io/dapr/testcontainers/DaprContainer.java | 41 +++++++ .../spring/DaprSidecarContainer.java | 67 +++++++++++ .../DaprSpringBootContextInitializer.java | 104 ++++++++++++++++++ .../spring/DaprSpringBootExtension.java | 88 +++++++++++++++ .../spring/DaprSpringBootTest.java | 79 +++++++++++++ 7 files changed, 434 insertions(+), 52 deletions(-) create mode 100644 testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSidecarContainer.java create mode 100644 testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootContextInitializer.java create mode 100644 testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootExtension.java create mode 100644 testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootTest.java diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/actors/DaprActorsIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/actors/DaprActorsIT.java index ba8cec619c..37960b761a 100644 --- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/actors/DaprActorsIT.java +++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/actors/DaprActorsIT.java @@ -20,64 +20,31 @@ import io.dapr.testcontainers.Component; import io.dapr.testcontainers.DaprContainer; import io.dapr.testcontainers.DaprLogLevel; +import io.dapr.testcontainers.spring.DaprSidecarContainer; +import io.dapr.testcontainers.spring.DaprSpringBootTest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Tag; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; -import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; -import org.springframework.test.context.DynamicPropertyRegistry; -import org.springframework.test.context.DynamicPropertySource; -import org.testcontainers.containers.Network; import org.testcontainers.containers.wait.strategy.Wait; -import org.testcontainers.junit.jupiter.Container; -import org.testcontainers.junit.jupiter.Testcontainers; import java.util.Map; -import java.util.Random; import java.util.UUID; -import static io.dapr.it.testcontainers.ContainerConstants.DAPR_RUNTIME_IMAGE_TAG; import static org.junit.jupiter.api.Assertions.assertEquals; -@SpringBootTest( - webEnvironment = WebEnvironment.RANDOM_PORT, - classes = { - TestActorsApplication.class, - TestDaprActorsConfiguration.class - } -) -@Testcontainers +@DaprSpringBootTest(classes = {TestActorsApplication.class, TestDaprActorsConfiguration.class}) @Tag("testcontainers") public class DaprActorsIT { - private static final Network DAPR_NETWORK = Network.newNetwork(); - private static final Random RANDOM = new Random(); - private static final int PORT = RANDOM.nextInt(1000) + 8000; private static final String ACTORS_MESSAGE_PATTERN = ".*Actor runtime started.*"; - @Container - private static final DaprContainer DAPR_CONTAINER = new DaprContainer(DAPR_RUNTIME_IMAGE_TAG) - .withAppName("actor-dapr-app") - .withNetwork(DAPR_NETWORK) - .withComponent(new Component("kvstore", "state.in-memory", "v1", - Map.of("actorStateStore", "true"))) - .withDaprLogLevel(DaprLogLevel.DEBUG) - .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())) - .withAppChannelAddress("host.testcontainers.internal") - .withAppPort(PORT); - - /** - * Expose the Dapr ports to the host. - * - * @param registry the dynamic property registry - */ - @DynamicPropertySource - static void daprProperties(DynamicPropertyRegistry registry) { - registry.add("dapr.http.endpoint", DAPR_CONTAINER::getHttpEndpoint); - registry.add("dapr.grpc.endpoint", DAPR_CONTAINER::getGrpcEndpoint); - registry.add("server.port", () -> PORT); - } + @DaprSidecarContainer + private static final DaprContainer DAPR_CONTAINER = DaprContainer.createForSpringBootTest("actor-dapr-app") + .withComponent(new Component("kvstore", "state.in-memory", "v1", + Map.of("actorStateStore", "true"))) + .withDaprLogLevel(DaprLogLevel.DEBUG) + .withLogConsumer(outputFrame -> System.out.println(outputFrame.getUtf8String())); @Autowired private ActorClient daprActorClient; @@ -86,8 +53,8 @@ static void daprProperties(DynamicPropertyRegistry registry) { private ActorRuntime daprActorRuntime; @BeforeEach - public void setUp(){ - org.testcontainers.Testcontainers.exposeHostPorts(PORT); + public void setUp() { + org.testcontainers.Testcontainers.exposeHostPorts(DAPR_CONTAINER.getAppPort()); daprActorRuntime.registerActor(TestActorImpl.class); // Wait for actor runtime to start. diff --git a/testcontainers-dapr/pom.xml b/testcontainers-dapr/pom.xml index 786ec56a96..21e36d7182 100644 --- a/testcontainers-dapr/pom.xml +++ b/testcontainers-dapr/pom.xml @@ -15,6 +15,50 @@ jar + + + org.yaml + snakeyaml + + + org.testcontainers + testcontainers + + + + + + org.junit.jupiter + junit-jupiter-api + true + + + org.junit.platform + junit-platform-commons + true + + + org.testcontainers + junit-jupiter + true + + + org.springframework.boot + spring-boot-test + true + + + org.springframework + spring-test + true + + + org.springframework + spring-context + true + + + org.junit.jupiter junit-jupiter @@ -25,14 +69,6 @@ mockito-core test - - org.yaml - snakeyaml - - - org.testcontainers - testcontainers - diff --git a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java index 823f6f61f7..c117b3e5d2 100644 --- a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java +++ b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java @@ -30,6 +30,7 @@ import org.yaml.snakeyaml.Yaml; import java.io.IOException; +import java.net.ServerSocket; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -105,6 +106,46 @@ public DaprContainer(String image) { this(DockerImageName.parse(image)); } + /** + * Creates a DaprContainer pre-configured for Spring Boot integration tests. + * This factory method handles the common setup required for bidirectional + * communication between Spring Boot and the Dapr sidecar: + *
    + *
  • Allocates a free port for the Spring Boot application
  • + *
  • Configures the app channel address for container-to-host communication
  • + *
+ * + *

Example usage:

+ *
{@code
+   * @DaprSpringBootTest(classes = MyApp.class)
+   * class MyIT {
+   *     @DaprSidecarContainer
+   *     private static final DaprContainer DAPR = DaprContainer.createForSpringBootTest("my-app")
+   *         .withComponent(new Component("statestore", "state.in-memory", "v1", Map.of()));
+   * }
+   * }
+ * + * @param appName the Dapr application name + * @return a pre-configured DaprContainer for Spring Boot tests + */ + public static DaprContainer createForSpringBootTest(String appName) { + int port = allocateFreePort(); + + return new DaprContainer(DAPR_RUNTIME_IMAGE_TAG) + .withAppName(appName) + .withAppPort(port) + .withAppChannelAddress("host.testcontainers.internal"); + } + + private static int allocateFreePort() { + try (ServerSocket socket = new ServerSocket(0)) { + socket.setReuseAddress(true); + return socket.getLocalPort(); + } catch (IOException e) { + throw new IllegalStateException("Failed to allocate free port", e); + } + } + public Configuration getConfiguration() { return configuration; } diff --git a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSidecarContainer.java b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSidecarContainer.java new file mode 100644 index 0000000000..2fd2ee47f1 --- /dev/null +++ b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSidecarContainer.java @@ -0,0 +1,67 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.testcontainers.spring; + +import org.testcontainers.junit.jupiter.Container; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Marks a static field containing a {@link io.dapr.testcontainers.DaprContainer} + * for automatic integration with Spring Boot tests. + * + *

This annotation combines the Testcontainers {@link Container} annotation + * with Dapr-specific configuration. When used with {@link DaprSpringBootTest}, + * it automatically:

+ *
    + *
  • Manages the container lifecycle via Testcontainers
  • + *
  • Configures Spring properties (server.port, dapr.http.endpoint, dapr.grpc.endpoint)
  • + *
+ * + *

Important: For tests that require Dapr-to-app communication (like actor tests), + * you must call {@code Testcontainers.exposeHostPorts(container.getAppPort())} + * in your {@code @BeforeEach} method before registering actors or making Dapr calls.

+ * + *

Example usage:

+ *
{@code
+ * @DaprSpringBootTest(classes = MyApplication.class)
+ * class MyDaprIT {
+ *
+ *     @DaprSidecarContainer
+ *     private static final DaprContainer DAPR = DaprContainer.createForSpringBootTest("my-app")
+ *         .withComponent(new Component("statestore", "state.in-memory", "v1", Map.of()));
+ *
+ *     @BeforeEach
+ *     void setUp() {
+ *         Testcontainers.exposeHostPorts(DAPR.getAppPort());
+ *     }
+ *
+ *     @Test
+ *     void testSomething() {
+ *         // Your test code here
+ *     }
+ * }
+ * }
+ * + * @see DaprSpringBootTest + * @see io.dapr.testcontainers.DaprContainer#createForSpringBootTest(String) + */ +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +@Container +public @interface DaprSidecarContainer { +} diff --git a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootContextInitializer.java b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootContextInitializer.java new file mode 100644 index 0000000000..1fa0ffb453 --- /dev/null +++ b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootContextInitializer.java @@ -0,0 +1,104 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.testcontainers.spring; + +import io.dapr.testcontainers.DaprContainer; +import org.springframework.context.ApplicationContextInitializer; +import org.springframework.context.ConfigurableApplicationContext; +import org.springframework.core.env.MapPropertySource; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Supplier; + +/** + * Spring {@link ApplicationContextInitializer} that configures Dapr-related properties + * based on the {@link DaprContainer} registered by {@link DaprSpringBootExtension}. + * + *

This initializer sets the following properties:

+ *
    + *
  • {@code server.port} - The port allocated for the Spring Boot application
  • + *
  • {@code dapr.http.endpoint} - The HTTP endpoint of the Dapr sidecar
  • + *
  • {@code dapr.grpc.endpoint} - The gRPC endpoint of the Dapr sidecar
  • + *
+ * + *

This initializer is automatically registered when using {@link DaprSpringBootTest}.

+ */ +public class DaprSpringBootContextInitializer + implements ApplicationContextInitializer { + + private static final String PROPERTY_SOURCE_NAME = "daprTestcontainersProperties"; + + @Override + public void initialize(ConfigurableApplicationContext applicationContext) { + DaprContainer container = findContainer(); + + if (container == null) { + throw new IllegalStateException( + "No DaprContainer found in registry. Ensure you are using @DaprSpringBootTest " + + "with a @DaprSidecarContainer annotated field." + ); + } + + // Create a property source with lazy resolution for endpoints + // server.port can be resolved immediately since it's set at container creation time + // Dapr endpoints are resolved lazily since the container may not be started yet + applicationContext.getEnvironment().getPropertySources() + .addFirst(new DaprLazyPropertySource(PROPERTY_SOURCE_NAME, container)); + } + + private DaprContainer findContainer() { + // Return the first container in the registry + // In a test scenario, there should only be one test class running at a time + return DaprSpringBootExtension.CONTAINER_REGISTRY.values().stream() + .findFirst() + .orElse(null); + } + + /** + * Custom PropertySource that lazily resolves Dapr container endpoints. + * This allows the endpoints to be resolved after the container has started. + */ + private static class DaprLazyPropertySource extends MapPropertySource { + private final Map> lazyProperties; + + DaprLazyPropertySource(String name, DaprContainer container) { + super(name, new HashMap<>()); + + this.lazyProperties = new HashMap<>(); + lazyProperties.put("server.port", container::getAppPort); + lazyProperties.put("dapr.http.endpoint", container::getHttpEndpoint); + lazyProperties.put("dapr.grpc.endpoint", container::getGrpcEndpoint); + } + + @Override + public Object getProperty(String name) { + Supplier supplier = lazyProperties.get(name); + if (supplier != null) { + return supplier.get(); + } + return null; + } + + @Override + public boolean containsProperty(String name) { + return lazyProperties.containsKey(name); + } + + @Override + public String[] getPropertyNames() { + return lazyProperties.keySet().toArray(new String[0]); + } + } +} diff --git a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootExtension.java b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootExtension.java new file mode 100644 index 0000000000..6dcafa4918 --- /dev/null +++ b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootExtension.java @@ -0,0 +1,88 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.testcontainers.spring; + +import io.dapr.testcontainers.DaprContainer; +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.platform.commons.support.AnnotationSupport; + +import java.lang.reflect.Field; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * JUnit 5 extension that handles Dapr container setup for Spring Boot tests. + * + *

This extension:

+ *
    + *
  • Discovers fields annotated with {@link DaprSidecarContainer}
  • + *
  • Registers the container for property injection by {@link DaprSpringBootContextInitializer}
  • + *
+ * + *

This extension is automatically registered when using {@link DaprSpringBootTest}.

+ */ +public class DaprSpringBootExtension implements BeforeAllCallback { + + /** + * Registry of DaprContainers by test class. Used by {@link DaprSpringBootContextInitializer} + * to configure Spring properties. + */ + static final Map, DaprContainer> CONTAINER_REGISTRY = new ConcurrentHashMap<>(); + + @Override + public void beforeAll(ExtensionContext context) throws Exception { + Class testClass = context.getRequiredTestClass(); + + // Find fields annotated with @DaprSidecarContainer + List containerFields = AnnotationSupport.findAnnotatedFields( + testClass, + DaprSidecarContainer.class, + field -> DaprContainer.class.isAssignableFrom(field.getType()) + ); + + if (containerFields.isEmpty()) { + throw new IllegalStateException( + "No @DaprSidecarContainer annotated field of type DaprContainer found in " + testClass.getName() + + ". Add a static field like: @DaprSidecarContainer private static final DaprContainer DAPR = " + + "DaprContainer.createForSpringBootTest(\"my-app\");" + ); + } + + if (containerFields.size() > 1) { + throw new IllegalStateException( + "Multiple @DaprSidecarContainer annotated fields found in " + testClass.getName() + + ". Only one DaprContainer per test class is supported." + ); + } + + Field containerField = containerFields.get(0); + containerField.setAccessible(true); + + DaprContainer container = (DaprContainer) containerField.get(null); + + if (container == null) { + throw new IllegalStateException( + "@DaprSidecarContainer field '" + containerField.getName() + "' is null in " + testClass.getName() + ); + } + + // Register container for the context initializer + CONTAINER_REGISTRY.put(testClass, container); + + // Note: Testcontainers.exposeHostPorts() is NOT called here because of timing requirements. + // It must be called in @BeforeEach, after the container starts to ensure proper Dapr-to-app communication. + } +} diff --git a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootTest.java b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootTest.java new file mode 100644 index 0000000000..a76552645a --- /dev/null +++ b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootTest.java @@ -0,0 +1,79 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.testcontainers.spring; + +import org.junit.jupiter.api.extension.ExtendWith; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.boot.test.context.SpringBootTest.WebEnvironment; +import org.springframework.core.annotation.AliasFor; +import org.springframework.test.context.ContextConfiguration; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * Composed annotation that combines {@link SpringBootTest}, {@link Testcontainers}, + * and the necessary extensions for Dapr integration testing. + * + *

This annotation simplifies the setup of Spring Boot integration tests with Dapr + * by handling port allocation, property configuration, and container lifecycle automatically.

+ * + *

Example usage:

+ *
{@code
+ * @DaprSpringBootTest(classes = MyApplication.class)
+ * class MyDaprIT {
+ *
+ *     @DaprSidecarContainer
+ *     private static final DaprContainer DAPR = DaprContainer.createForSpringBootTest("my-app")
+ *         .withComponent(new Component("statestore", "state.in-memory", "v1", Map.of()));
+ *
+ *     @Test
+ *     void testSomething() {
+ *         // Your test code here
+ *     }
+ * }
+ * }
+ * + * @see DaprSidecarContainer + * @see io.dapr.testcontainers.DaprContainer#createForSpringBootTest(String) + */ +@Target(ElementType.TYPE) +@Retention(RetentionPolicy.RUNTIME) +@ExtendWith(DaprSpringBootExtension.class) // Must be first to register container before Spring starts +@Testcontainers // Starts containers via @Container/@DaprSidecarContainer +@ContextConfiguration(initializers = DaprSpringBootContextInitializer.class) +@SpringBootTest(webEnvironment = WebEnvironment.DEFINED_PORT) // Starts Spring context last +public @interface DaprSpringBootTest { + + /** + * The application classes to use for the test. + * Alias for {@link SpringBootTest#classes()}. + * + * @return the application classes + */ + @AliasFor(annotation = SpringBootTest.class, attribute = "classes") + Class[] classes() default {}; + + /** + * Additional properties to configure the test. + * Alias for {@link SpringBootTest#properties()}. + * + * @return additional properties + */ + @AliasFor(annotation = SpringBootTest.class, attribute = "properties") + String[] properties() default {}; +} From cda34be377ed42be23e057834813edafc8a7d41a Mon Sep 17 00:00:00 2001 From: Artur Ciocanu Date: Wed, 24 Dec 2025 08:52:02 -0800 Subject: [PATCH 2/4] Adding DaprSpringBootTest and DaprSidecarContainer annotation for easier IT authoring. Signed-off-by: Artur Ciocanu --- testcontainers-dapr/pom.xml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/testcontainers-dapr/pom.xml b/testcontainers-dapr/pom.xml index 21e36d7182..fe02d62f4d 100644 --- a/testcontainers-dapr/pom.xml +++ b/testcontainers-dapr/pom.xml @@ -88,6 +88,20 @@ org.jacoco jacoco-maven-plugin + + + check + + check + + + + + io/dapr/testcontainers/spring/**/* + + + + From 0e8097443f5390f8df92e68a449fdf375066f46b Mon Sep 17 00:00:00 2001 From: Artur Ciocanu Date: Wed, 24 Dec 2025 09:12:36 -0800 Subject: [PATCH 3/4] Move all the helper Dapr SpringBoot annotations to tests, to avoid exposing it as public API Signed-off-by: Artur Ciocanu --- sdk-tests/pom.xml | 5 ++ .../spring/DaprSidecarContainer.java | 0 .../DaprSpringBootContextInitializer.java | 0 .../spring/DaprSpringBootExtension.java | 0 .../spring/DaprSpringBootTest.java | 0 testcontainers-dapr/pom.xml | 47 ------------------- .../io/dapr/testcontainers/DaprContainer.java | 9 ++-- 7 files changed, 8 insertions(+), 53 deletions(-) rename {testcontainers-dapr/src/main => sdk-tests/src/test}/java/io/dapr/testcontainers/spring/DaprSidecarContainer.java (100%) rename {testcontainers-dapr/src/main => sdk-tests/src/test}/java/io/dapr/testcontainers/spring/DaprSpringBootContextInitializer.java (100%) rename {testcontainers-dapr/src/main => sdk-tests/src/test}/java/io/dapr/testcontainers/spring/DaprSpringBootExtension.java (100%) rename {testcontainers-dapr/src/main => sdk-tests/src/test}/java/io/dapr/testcontainers/spring/DaprSpringBootTest.java (100%) diff --git a/sdk-tests/pom.xml b/sdk-tests/pom.xml index 29d0511358..f22e42a6c8 100644 --- a/sdk-tests/pom.xml +++ b/sdk-tests/pom.xml @@ -143,6 +143,11 @@ org.testcontainers junit-jupiter + + io.dapr + testcontainers-dapr + test + org.springframework.data spring-data-keyvalue diff --git a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSidecarContainer.java b/sdk-tests/src/test/java/io/dapr/testcontainers/spring/DaprSidecarContainer.java similarity index 100% rename from testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSidecarContainer.java rename to sdk-tests/src/test/java/io/dapr/testcontainers/spring/DaprSidecarContainer.java diff --git a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootContextInitializer.java b/sdk-tests/src/test/java/io/dapr/testcontainers/spring/DaprSpringBootContextInitializer.java similarity index 100% rename from testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootContextInitializer.java rename to sdk-tests/src/test/java/io/dapr/testcontainers/spring/DaprSpringBootContextInitializer.java diff --git a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootExtension.java b/sdk-tests/src/test/java/io/dapr/testcontainers/spring/DaprSpringBootExtension.java similarity index 100% rename from testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootExtension.java rename to sdk-tests/src/test/java/io/dapr/testcontainers/spring/DaprSpringBootExtension.java diff --git a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootTest.java b/sdk-tests/src/test/java/io/dapr/testcontainers/spring/DaprSpringBootTest.java similarity index 100% rename from testcontainers-dapr/src/main/java/io/dapr/testcontainers/spring/DaprSpringBootTest.java rename to sdk-tests/src/test/java/io/dapr/testcontainers/spring/DaprSpringBootTest.java diff --git a/testcontainers-dapr/pom.xml b/testcontainers-dapr/pom.xml index fe02d62f4d..ebc6887b9a 100644 --- a/testcontainers-dapr/pom.xml +++ b/testcontainers-dapr/pom.xml @@ -25,39 +25,6 @@ testcontainers - - - - org.junit.jupiter - junit-jupiter-api - true - - - org.junit.platform - junit-platform-commons - true - - - org.testcontainers - junit-jupiter - true - - - org.springframework.boot - spring-boot-test - true - - - org.springframework - spring-test - true - - - org.springframework - spring-context - true - - org.junit.jupiter @@ -88,20 +55,6 @@ org.jacoco jacoco-maven-plugin - - - check - - check - - - - - io/dapr/testcontainers/spring/**/* - - - - diff --git a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java index c117b3e5d2..6ac3eca72c 100644 --- a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java +++ b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java @@ -117,12 +117,9 @@ public DaprContainer(String image) { * *

Example usage:

*
{@code
-   * @DaprSpringBootTest(classes = MyApp.class)
-   * class MyIT {
-   *     @DaprSidecarContainer
-   *     private static final DaprContainer DAPR = DaprContainer.createForSpringBootTest("my-app")
-   *         .withComponent(new Component("statestore", "state.in-memory", "v1", Map.of()));
-   * }
+   * @Container
+   * private static final DaprContainer DAPR = DaprContainer.createForSpringBootTest("my-app")
+   *     .withComponent(new Component("statestore", "state.in-memory", "v1", Map.of()));
    * }
* * @param appName the Dapr application name From 72127008fb848ae89ccc9adae517de0caec29d98 Mon Sep 17 00:00:00 2001 From: Artur Ciocanu Date: Wed, 24 Dec 2025 09:44:20 -0800 Subject: [PATCH 4/4] Fix a few issues related to Dapr container usage in ITs. Signed-off-by: Artur Ciocanu --- .../testcontainers/actors/DaprActorsIT.java | 3 +- .../spring/DaprContainerFactory.java | 64 +++++++++++++++++++ .../io/dapr/testcontainers/DaprContainer.java | 38 ----------- 3 files changed, 66 insertions(+), 39 deletions(-) create mode 100644 sdk-tests/src/test/java/io/dapr/testcontainers/spring/DaprContainerFactory.java diff --git a/sdk-tests/src/test/java/io/dapr/it/testcontainers/actors/DaprActorsIT.java b/sdk-tests/src/test/java/io/dapr/it/testcontainers/actors/DaprActorsIT.java index 37960b761a..fe90b452c1 100644 --- a/sdk-tests/src/test/java/io/dapr/it/testcontainers/actors/DaprActorsIT.java +++ b/sdk-tests/src/test/java/io/dapr/it/testcontainers/actors/DaprActorsIT.java @@ -20,6 +20,7 @@ import io.dapr.testcontainers.Component; import io.dapr.testcontainers.DaprContainer; import io.dapr.testcontainers.DaprLogLevel; +import io.dapr.testcontainers.spring.DaprContainerFactory; import io.dapr.testcontainers.spring.DaprSidecarContainer; import io.dapr.testcontainers.spring.DaprSpringBootTest; import org.junit.jupiter.api.BeforeEach; @@ -40,7 +41,7 @@ public class DaprActorsIT { private static final String ACTORS_MESSAGE_PATTERN = ".*Actor runtime started.*"; @DaprSidecarContainer - private static final DaprContainer DAPR_CONTAINER = DaprContainer.createForSpringBootTest("actor-dapr-app") + private static final DaprContainer DAPR_CONTAINER = DaprContainerFactory.createForSpringBootTest("actor-dapr-app") .withComponent(new Component("kvstore", "state.in-memory", "v1", Map.of("actorStateStore", "true"))) .withDaprLogLevel(DaprLogLevel.DEBUG) diff --git a/sdk-tests/src/test/java/io/dapr/testcontainers/spring/DaprContainerFactory.java b/sdk-tests/src/test/java/io/dapr/testcontainers/spring/DaprContainerFactory.java new file mode 100644 index 0000000000..fdf9815f46 --- /dev/null +++ b/sdk-tests/src/test/java/io/dapr/testcontainers/spring/DaprContainerFactory.java @@ -0,0 +1,64 @@ +/* + * Copyright 2025 The Dapr Authors + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and +limitations under the License. +*/ + +package io.dapr.testcontainers.spring; + +import io.dapr.testcontainers.DaprContainer; + +import java.io.IOException; +import java.net.ServerSocket; + +import static io.dapr.testcontainers.DaprContainerConstants.DAPR_RUNTIME_IMAGE_TAG; + +/** + * Factory for creating DaprContainer instances configured for Spring Boot integration tests. + * + *

This class handles the common setup required for bidirectional communication + * between Spring Boot applications and the Dapr sidecar in test scenarios.

+ */ +public final class DaprContainerFactory { + + private DaprContainerFactory() { + // Utility class + } + + /** + * Creates a DaprContainer pre-configured for Spring Boot integration tests. + * This factory method handles the common setup required for bidirectional + * communication between Spring Boot and the Dapr sidecar: + *
    + *
  • Allocates a free port for the Spring Boot application
  • + *
  • Configures the app channel address for container-to-host communication
  • + *
+ * + * @param appName the Dapr application name + * @return a pre-configured DaprContainer for Spring Boot tests + */ + public static DaprContainer createForSpringBootTest(String appName) { + int port = allocateFreePort(); + + return new DaprContainer(DAPR_RUNTIME_IMAGE_TAG) + .withAppName(appName) + .withAppPort(port) + .withAppChannelAddress("host.testcontainers.internal"); + } + + private static int allocateFreePort() { + try (ServerSocket socket = new ServerSocket(0)) { + socket.setReuseAddress(true); + return socket.getLocalPort(); + } catch (IOException e) { + throw new IllegalStateException("Failed to allocate free port", e); + } + } +} diff --git a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java index 6ac3eca72c..823f6f61f7 100644 --- a/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java +++ b/testcontainers-dapr/src/main/java/io/dapr/testcontainers/DaprContainer.java @@ -30,7 +30,6 @@ import org.yaml.snakeyaml.Yaml; import java.io.IOException; -import java.net.ServerSocket; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; @@ -106,43 +105,6 @@ public DaprContainer(String image) { this(DockerImageName.parse(image)); } - /** - * Creates a DaprContainer pre-configured for Spring Boot integration tests. - * This factory method handles the common setup required for bidirectional - * communication between Spring Boot and the Dapr sidecar: - *
    - *
  • Allocates a free port for the Spring Boot application
  • - *
  • Configures the app channel address for container-to-host communication
  • - *
- * - *

Example usage:

- *
{@code
-   * @Container
-   * private static final DaprContainer DAPR = DaprContainer.createForSpringBootTest("my-app")
-   *     .withComponent(new Component("statestore", "state.in-memory", "v1", Map.of()));
-   * }
- * - * @param appName the Dapr application name - * @return a pre-configured DaprContainer for Spring Boot tests - */ - public static DaprContainer createForSpringBootTest(String appName) { - int port = allocateFreePort(); - - return new DaprContainer(DAPR_RUNTIME_IMAGE_TAG) - .withAppName(appName) - .withAppPort(port) - .withAppChannelAddress("host.testcontainers.internal"); - } - - private static int allocateFreePort() { - try (ServerSocket socket = new ServerSocket(0)) { - socket.setReuseAddress(true); - return socket.getLocalPort(); - } catch (IOException e) { - throw new IllegalStateException("Failed to allocate free port", e); - } - } - public Configuration getConfiguration() { return configuration; }