1

I have a SpringBoot app and I have added JUnit5 integration tests that use testcontainers:

class ControllerClassTest extends AbstractIntegrationTest {

    @ParameterizedTest(name = "{0}")
    @CsvSource({
            // ... test args ... 
    })
    @DisplayName("Happy flow")
    void testEndpoint(String... args) {
         // using rest assured
    }
@ActiveProfiles("test")
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = MainApplication.class)
public abstract class AbstractIntegrationTest {

    private static final GenericContainer<?> POSTGRES_INSTANCE = new GenericContainer<>(
            new ImageFromDockerfile("my-db-image", false)
                    .withDockerfile(Path.of("docker/my-db-setup.Dockerfile")))
            .withCreateContainerCmdModifier(cmd -> {
                cmd.withName("my-db-container");
                Objects.requireNonNull(cmd.getHostConfig())
                        .withPortBindings(new PortBinding(Ports.Binding.bindPort(5432), new ExposedPort(5432)));
            });

    private SqlScriptUtil scriptUtils;

    @Autowired
    @Qualifier("appDataSource")
    private DataSource dataSource;


    @PostConstruct
    void setupDataSource() {
        scriptUtils = new SqlScriptUtil(dataSource);
    }

    @BeforeAll
    static void init() {
        POSTGRES_INSTANCE.start();
    }

    /**
     * Helper method to setup a DB state
     */
    protected void setupDatabaseState(String dbBackup) {
        scriptUtils.executeScript("db/state/" + dbBackup + ".sql");
    }

   // others ... 
}

I need to decouple from the docker dependency for the team's local and the server development environment.

It is worth noting I have DB migration scripts using Liquibase and two changeset files that get executed in order - first one uses the admin DB user to create the DB schema and a custom user. Second one utilises the new user and creates the DB objects and other migration updates.

I tried going for an approach that utilises spring profiles and using embedded Postgres DB from io.zonky.test. That way I can keep the existing utilisation of test-containers and I can execute the tests quicker and other team members can execute them without having docker engine installed on their machine, but we can also execute them in a pipeline executor so that tests are more accurate and are more similar with actual running environment. Is this a correct approach? Am I making my life unnecessary harder?

What I have done from the above state is I have removed the testcontainer instance from the abstract test and created profile conditional test configurations for the database. However the tests fail as they cannot connect to a DB instance. For some reason the configuration beans do not get instantiated thus there is nothing to listen at the specific port:

@ActiveProfiles("test")
@Import({TestPostgresInstance.class})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = MainApplication.class)
public abstract class AbstractIntegrationTest {

    private SqlScriptUtil scriptUtils;

    @Autowired
    @Qualifier("appDataSource")
    private DataSource dataSource;


    @PostConstruct
    void setupDataSource() {
        scriptUtils = new SqlScriptUtil(dataSource);
    }

    /**
     * Helper method to setup a DB state
     */
    protected void setupDatabaseState(String dbBackup) {
        scriptUtils.executeScript("db/state/" + dbBackup + ".sql");
    }

   // others ... 
}
public interface TestPostgresInstance {
    int DB_PORT = 5432;
}
@TestConfiguration
@Profile("test & (local | dev)")
@AutoConfigureEmbeddedDatabase(
        provider = ZONKY,
        type = POSTGRES)
public class EmbeddedPostgresDb implements TestPostgresInstance {
}
@TestConfiguration
@Profile("test & !local & !dev")
public class DockerPostgresDb implements TestPostgresInstance {

    public DockerPostgresDb() {
        GenericContainer<?> dbInstance = GenericContainer<>(
            new ImageFromDockerfile("my-db-image", false)
                    .withDockerfile(Path.of("docker/my-db-setup.Dockerfile")))
            .withCreateContainerCmdModifier(cmd -> {
                cmd.withName("my-db-container");
                Objects.requireNonNull(cmd.getHostConfig())
                        .withPortBindings(new PortBinding(Ports.Binding.bindPort(5432), new ExposedPort(5432)));
            });
        dbInstance.start();
    }
}

0

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.