I have an issue related to https://github.com/spring-projects/spring-boot/issues/40585 . I am using Spring Boot 3.3.2, which should have the fix, however maybe my scenario is slightly different.

I have a bean which has @ConditionalOnProperty("x") and a test containers configuration which uses dynamic property source to inject the exact same proprty "x". What happens is that the conditional tries to resolve the property before the container is started and cannot access the mapped port.

Container configuration

public interface MockAuthServerContainerConfiguration {
  @Container
  MockAuthServerTestContainer MOCK_AUTH_SERVER_TEST_CONTAINER =
      new MockAuthServerTestContainer().withNetwork(Networks.BRIDGE);

  @DynamicPropertySource
  static void setContainerProperties(DynamicPropertyRegistry registry) {
    registry.add(
        "authentication.url",
        () -> String.format(
                "http://localhost:%d/", MOCK_AUTH_SERVER_TEST_CONTAINER.getFirstMappedPort()));
  }
}

Bean configuration

@Configuration
public class AuthenticationBean{

  @Bean
  @ConditionalOnMissingBean
  @ConditionalOnProperty("authentication.url")
  AuthenticationBean authenticationBean() {
             ...
  }
}

Test usage

@Testcontainers
@ImportTestcontainers({MockAuthServerContainerConfiguration .class})

The behavior is that trying to resolve the property in order to determine bean creation happens before the container is started and the port cannot be retrieved: "Mapped port can only be obtained after the container is started"

Comment From: wilkinsona

The scenario here is quite different as you're querying the value of a property in a condition. Conditions are evaluated very early – while the bean factory is being populated and before any beans exist – and this is too early for the infrastructure that automatically starts a container when a property that it provides is used.

If you want to be able to use @ConditionalOnProperty to query a container-provided property, the lifecycle of that container will have to be managed by Testcontainers rather than by the application context. Here's a minimal example of that:

@SpringBootTest
@Testcontainers
class Gh41664ApplicationTests {

    @Container
    private static MockAuthServerTestContainer container = new MockAuthServerTestContainer().withNetwork(Networks.BRIDGE);

    @DynamicPropertySource
    static void setContainerProperties(DynamicPropertyRegistry registry) {
        registry.add("authentication.url", () -> String.format("http://localhost:%d/", container.getFirstMappedPort()));
    }

    @Test
    void contextLoads() {
    }

}

Given your use of @Testcontainers and @Container above, perhaps this what you were already striving for? Unfortunately, it's not clear with only a few code snippets to look at.

Comment From: MrSinisterX

The scenario here is quite different as you're querying the value of a property in a condition. Conditions are evaluated very early – while the bean factory is being populated and before any beans exist – and this is too early for the infrastructure that automatically starts a container when a property that it provides is used.

If you want to be able to use @ConditionalOnProperty to query a container-provided property, the lifecycle of that container will have to be managed by Testcontainers rather than by the application context. Here's a minimal example of that:

```java @SpringBootTest @Testcontainers class Gh41664ApplicationTests {

@Container private static MockAuthServerTestContainer container = new MockAuthServerTestContainer().withNetwork(Networks.BRIDGE);

@DynamicPropertySource static void setContainerProperties(DynamicPropertyRegistry registry) { registry.add("authentication.url", () -> String.format("http://localhost:%d/", container.getFirstMappedPort())); }

@Test void contextLoads() { }

} ```

Given your use of @Testcontainers and @Container above, perhaps this what you were already striving for? Unfortunately, it's not clear with only a few code snippets to look at.

Yes, you are right. It is working like this. My goal here was other. I wanted to be able to avoid having this container and the DynamicPropertySource declared in all the test classes where this is needed. I wanted to use the approach with the configuration so that I can reuse the same setup everywhere, without explicitly duplicating that code everywhere... but as you mentioned it is not possible.

With other containers that do not influence beans via conditional checking everything is working ok. So, I guess for these scenarios I have to stick with this.

With the @ImportTestContainers approach I was even able to define my own annotation that provides all the setup to the test class.

e.g.

@WithMyContainer which inherits from @ImportTestContainers({ContainerConfig}) and @Testcontainers

Adding @WithMyContainer to any test class would just provide the container to that test class.

Thank you for your answer!

Comment From: wilkinsona

I wanted to be able to avoid having this container and the DynamicPropertySource declared in all the test classes where this is needed

You can use an interface for this:

interface Containers {

    @Container
    static MockAuthServerTestContainer container = new MockAuthServerTestContainer().withNetwork(Networks.BRIDGE);

    @DynamicPropertySource
    static void setContainerProperties(DynamicPropertyRegistry registry) {
        registry.add("authentication.url", () -> String.format("http://localhost:%d/", container.getFirstMappedPort()));
    }

}

A test class can then implement this interface:

@SpringBootTest
@Testcontainers
class Gh41664ApplicationTests implements Containers {

    @Test
    void contextLoads() {
    }

}

This allows the configuration to be shared while also allowing Testcontainers to manage the container lifecycle.

I don't think there's anything that we can do in Spring Boot to improve this so I'll close this one.

Comment From: MrSinisterX

I wanted to be able to avoid having this container and the DynamicPropertySource declared in all the test classes where this is needed

You can use an interface for this:

``` interface Containers {

@Container static MockAuthServerTestContainer container = new MockAuthServerTestContainer().withNetwork(Networks.BRIDGE);

@DynamicPropertySource static void setContainerProperties(DynamicPropertyRegistry registry) { registry.add("authentication.url", () -> String.format("http://localhost:%d/", container.getFirstMappedPort())); }

} ```

A test class can then implement this interface:

``` @SpringBootTest @Testcontainers class Gh41664ApplicationTests implements Containers {

@Test void contextLoads() { }

} ```

This allows the configuration to be shared while also allowing Testcontainers to manage the container lifecycle.

I don't think there's anything that we can do in Spring Boot to improve this so I'll close this one.

This doesn't seem to be working since static void setContainerProperties(DynamicPropertyRegistry registry) is a static method and when Testcontainers creates a new container for another test class, then the spring context doesn't get updated with the new port number, as this only gets executed once. The services are pointing to the "old" container that was created first.

Comment From: wilkinsona

I would guess that's due to context caching and that the affected tests have otherwise identical configuration. You may need to use @DirtiesContext so that each test gets it own context and the lifecycles will then align. I think this is another variant of https://github.com/spring-projects/spring-boot/issues/38237.

Comment From: MrSinisterX

I would guess that's due to context caching and that the affected tests have otherwise identical configuration. You may need to use @DirtiesContext so that each test gets it own context and the lifecycles will then align. I think this is another variant of #38237.

Using @DirtiesContext works, however using spring.test.context.cache.maxSize=1 as you used in https://github.com/spring-projects/spring-boot/commit/138307c13cd04fd21a3496bd6c0e3398a345a20c doesn't.

I also believe that having a mechanism to reuse container configurations for those containers that dont't rely on @ServiceConnection would be nice in terms of avoiding code duplication and providing a good maintainability of the codebase.