Testcontainers is a JVM library that allows users to run and manage Docker images and control them from Java code. The integration test additionally runs external components as real Docker containers.
package com.test;
import org.springframework.boot.test.autoconfigure.jdbc.AutoConfigureTestDatabase;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.context.ApplicationContextInitializer;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.PropertySource;
import org.springframework.test.context.ActiveProfiles;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.support.TestPropertySourceUtils;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.testcontainers.containers.PostgreSQLContainer;
import org.testcontainers.ext.ScriptUtils;
import org.testcontainers.jdbc.JdbcDatabaseDelegate;
import org.testcontainers.junit.jupiter.Testcontainers;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper;
import org.testcontainers.shaded.com.fasterxml.jackson.databind.SerializationFeature;
import java.util.Optional;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
@Testcontainers
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT, classes = {com.test.Application.class})
@ActiveProfiles(AbstractBaseIntergrationTestConfiguration.ACTIVE_PROFILE_NAME_TEST)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@ContextConfiguration(initializers = AbstractBaseIntergrationTestConfiguration.DockerPostgreDataSourceInitializer.class)
public abstract class AbstractBaseIntergrationTestConfiguration {
protected static final String JDBC_URL = "jdbc.url=";
protected static final String JDBC_USERNAME = "jdbc.username=";
protected static final String JDBC_PASSWORD = "jdbc.password=";
protected static final String JDBC_DRIVER_CLASS_NAME_ORG_POSTGRESQL_DRIVER = "jdbc.driverClassName=org.postgresql.Driver";
protected static final String ACTIVE_PROFILE_NAME_TEST = "TestContainerTests";
//--
public static PostgreSQLContainer<?> postgreDBContainer;
protected ObjectMapper objectMapper = new ObjectMapper().disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
static {
// Init DB Script here
postgreDBContainer = new PostgreSQLContainer<>(IntegrationTestConstants.POSTGRESQL_IMAGE);
postgreDBContainer
.withInitScript(IntegrationTestConstants.INIT_DB_SCRIPT)
.withDatabaseName(IntegrationTestConstants.DB_NAME)
.withUsername(IntegrationTestConstants.DB_USERNAME)
.withPassword(IntegrationTestConstants.DB_PASSWORD);
postgreDBContainer.start();
var containerDelegate = new JdbcDatabaseDelegate(postgreDBContainer, "");
// Adding Database scripts here
ScriptUtils.runInitScript(containerDelegate, IntegrationTestConstants.MISSING_TABLES_SQL);
ScriptUtils.runInitScript(containerDelegate, IntegrationTestConstants.SAMPLE_DATA_SQL);
}
// This class adds the DB properties to Testcontainers.
public static class DockerPostgreDataSourceInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
TestPropertySourceUtils.addInlinedPropertiesToEnvironment(
applicationContext,
JDBC_DRIVER_CLASS_NAME_ORG_POSTGRESQL_DRIVER,
JDBC_URL + postgreDBContainer.getJdbcUrl(),
JDBC_USERNAME + postgreDBContainer.getUsername(),
JDBC_PASSWORD + postgreDBContainer.getPassword()
);
}
}
}
@Test
void checkIfUserExistInIdealCase() throws Exception {
final JSONObject request = new JSONObject();
request.put("email", "abc@test.com");
final MockHttpServletRequestBuilder postObject = getPostRequestExecutorBuilder("http://localhost:8080/v1/checkemail/", Optional.empty());
final MvcResult result = mockMvc.perform(postObject.content(request.toString())).andExpect(status().isOk()).andReturn();
final String content = result.getResponse().getContentAsString();
final SyncResponseDto responseDto = objectMapper.readValue(content, SyncResponseDto.class);
assertThat(responseDto.getResponseReturnCode()).isEqualTo(ResponseReturnCode.USER\_EXIST);
}
Multiple containers can be added and it’s consistent across all developer machines. Same versions etc and runs without any efforts with GitHub actions.