import static org.assertj.core.api.Assertions.assertThat;

import javax.sql.DataSource;

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.TestConfiguration;
import org.springframework.boot.testcontainers.service.connection.ServiceConnection;
import org.springframework.context.annotation.Bean;
import org.springframework.core.env.Environment;
import org.testcontainers.containers.GenericContainer;
import org.testcontainers.containers.MySQLContainer;

import com.zaxxer.hikari.HikariDataSource;

@SpringBootTest
public class ApplicationTests {

    @Autowired
    private DataSource dataSource;

    @Test
    void test() {
        assertThat(dataSource).isInstanceOfSatisfying(HikariDataSource.class,
                ds -> assertThat(ds.getJdbcUrl()).startsWith("jdbc:mysql://"));
    }

    @TestConfiguration
    static class Config {

        @Autowired
        Environment env;

        @Bean
        @ServiceConnection
        GenericContainer<?> mysql() {
            assertThat(env).isNotNull(); // assertion failed
            return new MySQLContainer<>("mysql:5.7");
        }
    }
}

Inject Environment env by method parameter instead works fine. Remove @ServiceConnection will make field injection works fine but dataSource not wanted.

Comment From: wilkinsona

Thanks for the report, @quaff.

I'm not sure what we can do about this. @ServiceConnection-annotated beans are loaded by an ImportBeanDefinitionRegistrar so that the relevant …ConnectionDetails beans can be defined for the service. This results in the beans being created very early, crucially before auto-wiring has been performed on the containing configuration class.

One option that doesn't solve the problem but that would make the behavior more clear would be to require the methods to be static, failing fast if they're not. This would be a slightly stricter variant of Framework's recommendation for BeanPostProcessor and BeanFactoryPostProcessor:

Also, be particularly careful with BeanPostProcessor and BeanFactoryPostProcessor definitions through @Bean. Those should usually be declared as static @Bean methods, not triggering the instantiation of their containing configuration class. Otherwise, @Autowired and @Value may not work on the configuration class itself, since it is possible to create it as a bean instance earlier than AutowiredAnnotationBeanPostProcessor.

Comment From: snicoll

@philwebb can you share the outcome of the discussion? I am concerned about the getBean call in the bean definition phase.

Comment From: philwebb

The main outcome was we want to remove the call, but we're not sure how yet.

Comment From: philwebb

It's possible that the future direction of Testcontainers will further decouple the container definition from a started container. With that in mind, not accessing the Container at all would be best. We should also switch to the parent ContainerState interface if possible.