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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.