Giriş
Test @TestContainers anotasyonu mecburen eklenir
Maven
Örnek
Şu satırı dahil ederiz
<properties>
<testcontainers.version>1.16.0</testcontainers.version>
</properties>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers-bom</artifactId>
<version>${testcontainers.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Örnek
Eğer dependencyManagement kullanmak istemiyorsak şöyle
yaparız<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>testcontainers</artifactId>
<version>1.16.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>junit-jupiter</artifactId>
<version>1.16.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.16.3</version>
<scope>test</scope>
</dependency>
TestContainer ile PostgreSQL kullanmak için iki yöntem var.
1. Test veri tabanı bilgisi src/test/resources/application.yml altında saklanır
2. Teste @Container anotasyonu eklenir
3. TestContainer Sınıfından Kalıtma
1. Yml Kullanımı
Örnek
limizde şöyle bir application.yml
olsun. Burada jdbc connection string "tc" ile başlıyor.
spring.datasource.url=jdbc:tc:postgresql://localhost/testdb
spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver
spring.datasource.username=user
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=update
Örnek
Elimizde şöyle bir application.yml
olsun. Burada jdbc connection string "tc" ile başlıyor.
spring:
datasource:
url: jdbc:tc:postgresql:9.6.8:///test_database
username: user
password: password
jpa:
hibernate:
ddl-auto: create
Have you noticed the tc suffix in the JDBC-connection string? That’s the magic that comes with the union of JUnit 5 and Test containers. The thing is that you don’t need any programmatic configurations at all! When the framework sees that url contains the tc suffix it runs all necessary Docker commands internally.
Elimizde şöyle bir service kodu
olsun
@Service
@RequiredArgsConstructor
public class PersonCreateServiceImpl implements PersonCreateService {
private final PersonValidateService personValidateService;
private final PersonRepository personRepository;
@Override
@Transactional
public List<PersonDTO> createFamily(Iterable<String> firstNames, String lastName) {
final var people = new ArrayList<PersonDTO>();
firstNames.forEach(firstName -> people.add(createPerson(firstName, lastName)));
return people;
}
@Override
@Transactional
public PersonDTO createPerson(String firstName, String lastName) {
personValidateService.checkUserCreation(firstName, lastName);
final var createdPerson = personRepository.saveAndFlush(
new Person()
.setFirstName(firstName)
.setLastName(lastName)
);
return DTOConverters.toPersonDTO(createdPerson);
}
}
Bu service koduna H2 veri tabanı ile test yazsaydık şöyle
olurdu. Burada repository sınıfı ve service sınıfı @Autowired ile aynen kullanılıyor. Service sınıfının kullandığı PersonValidateService ile mock'lanıyor. @AutoConfigureTestDatabase ile test için veri tabanı yaratılıyor.
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
@AutoConfigureTestDatabase
class PersonCreateServiceImplSpringBootTest {
@Autowired
private PersonRepository personRepository;
@MockBean
private PersonValidateService personValidateService;
@Autowired
private PersonCreateService personCreateService;
@BeforeEach
void init() {
personRepository.deleteAll();
}
@Test
void shouldCreateOnePerson() {
final var people = personCreateService.createFamily(List.of("Simon"),"Kirekov");
assertEquals(1, people.size());
final var person = people.get(0);
assertEquals("Simon", person.getFirstName());
assertEquals("Kirekov", person.getLastName());
assertTrue(person.getDateCreated().isBefore(ZonedDateTime.now()));
}
@Test
void shouldRollbackIfAnyUserIsNotValidated() {
doThrow(new ValidationFailedException(""))
.when(personValidateService)
.checkUserCreation("John", "Brown");
assertThrows(ValidationFailedException.class, () -> personCreateService.createFamily(
List.of("Matilda", "Vasya", "John"),
"Brown"
));
assertEquals(0, personRepository.count());
}
}
Bu testi TestContainers ile çalışacak hale getirmek için şöyle
yaparız. ActiveProfiles ile yaml dosyası kullanılır.
@SpringBootTest(webEnvironment = WebEnvironment.NONE)
@Testcontainers
@ActiveProfiles("test-containers")
class PersonCreateServiceImplTestContainers {
@Autowired
private PersonRepository personRepository;
@MockBean
private PersonValidateService personValidateService;
@Autowired
private PersonCreateService personCreateService;
@BeforeEach
void init() {
personRepository.deleteAll();
}
@Test
void shouldCreateOnePerson() {
final var people = personCreateService.createFamily(
List.of("Simon"),"Kirekov");
assertEquals(1, people.size());
final var person = people.get(0);
assertEquals("Simon", person.getFirstName());
assertEquals("Kirekov", person.getLastName());
assertTrue(person.getDateCreated().isBefore(ZonedDateTime.now()));
}
@Test
void shouldRollbackIfAnyUserIsNotValidated() {
doThrow(new ValidationFailedException(""))
.when(personValidateService)
.checkUserCreation("John", "Brown");
assertThrows(ValidationFailedException.class, () -> personCreateService.createFamily(
List.of("Matilda", "Vasya", "John"),
"Brown"
));
assertEquals(0, personRepository.count());
}
}
2. @Container Kullanımı
Repository sınıfı @Autowire ile test'e dahil edilir.
Örnek
Şöyle
yaparız. Burada PostgreSQLContainer() sürümü veriliyor
// JUnit 5 example with Spring Boot >= 2.2.6
@Testcontainers
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
public class ApplicationIT {
@Container
public static PostgreSQLContainer container = new PostgreSQLContainer("postgres:11.1")
.withPassword("inmemory")
.withUsername("inmemory");
@DynamicPropertySource
static void postgresqlProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", container::getJdbcUrl);
registry.add("spring.datasource.password", container::getPassword);
registry.add("spring.datasource.username", container::getUsername);
}
...
}
Eğer DataSource ta yaratmak istersek şöyle
yaparız@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@Testcontainers
@Import(Configuration.class)
public abstract class BaseApiTest {
@Container
static final PostgreSQLContainer<?> postgres =
new PostgreSQLContainer<>("postgres:10.9");
@DynamicPropertySource
static void properties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgres::getJdbcUrl);
registry.add("spring.datasource.username", postgres::getUsername);
registry.add("spring.datasource.password", postgres::getPassword);
}
@TestConfiguration
public static class Configuration {
@Lazy
@Bean("testSetupDataSource")
public DataSource testInitDataSource() {
PGSimpleDataSource ds = new PGSimpleDataSource();
ds.setUrl(postgres.getJdbcUrl());
ds.setDatabaseName(postgres.getDatabaseName());
ds.setUser(postgres.getUsername());
ds.setPassword(postgres.getPassword());
return ds;
}
}
}Örnek
@DataJdbcTest
@Import(DatasourceConfig.class)
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
@Testcontainers
@DisplayName("Comment Repository Integration Tests With TestContainers")
class CommentRepositoryTest {
@Container
static PostgreSQLContainer<?> postgresContainer =
new PostgreSQLContainer<>(DockerImageName.parse("postgres:15.1"));
@DynamicPropertySource
static void overridePostgresProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgresContainer::getJdbcUrl);
registry.add("spring.datasource.username", postgresContainer::getUsername);
registry.add("spring.datasource.password", postgresContainer::getPassword);
registry.add("spring.flyway.url", postgresContainer::getJdbcUrl);
}
@Autowired
private CommentRepository commentRepository;
...
}
@DataJdbcTest focuses only on Data JDBC components.
@Import(DatasourceConfig.class) provides JDBC auditing support.
@AutoConfigureTestDatabase disables in-memory test databases with AutoConfigureTestDatabase.Replace.NONE value. Because we want to use real database instance instead of in-memory database.
Örnek
Şöyle
yaparız. Liquibase veri tabanını test için doldurur
@Testcontainers
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes =
ContainerSpringAfter226Test.class)
@EnableAutoConfiguration
public class ContainerSpringAfter226Test {
@Container
public static PostgreSQLContainer postgreSQLContainer =
SharedPostgresSQLContainer.getInstance()
.withDatabaseName("dbname")
.withUsername("user")
.withPassword("password");
@DynamicPropertySource
public static void postgreSQLProperties(DynamicPropertyRegistry registry) {
registry.add("spring.datasource.url", postgreSQLContainer::getJdbcUrl);
registry.add("spring.datasource.username", postgreSQLContainer::getUsername);
registry.add("spring.datasource.password", postgreSQLContainer::getPassword);
registry.add("spring.liquibase.url", postgreSQLContainer::getJdbcUrl);
registry.add("spring.liquibase.user", postgreSQLContainer::getUsername);
registry.add("spring.liquibase.password", postgreSQLContainer::getPassword);
registry.add("spring.liquibase.change-log",
() -> "classpath:/db/changelog/db.changelog-master.xml");
}
...
}
3. TestContainer Sınıfından KalıtmaÖrnek
@TestConfiguration
public class PostgresSQLTestContainerConfig
extends PostgreSQLContainer<PostgresSQLTestContainerConfig> {
private static final String DOCKER_IMAGE_VERSION = "postgres:latest";
private static final PostgresSQLTestContainerConfig POSTGRES_SQL_TEST_CONTAINER =
new PostgresSQLTestContainerConfig().withReuse(true);
static {
POSTGRES_SQL_TEST_CONTAINER.start();
System.setProperty("spring.datasource.url", POSTGRES_SQL_TEST_CONTAINER.getJdbcUrl());
System.setProperty("spring.datasource.username",
POSTGRES_SQL_TEST_CONTAINER.getUsername());
System.setProperty("spring.datasource.password",
POSTGRES_SQL_TEST_CONTAINER.getPassword());
}
public PostgresSQLTestContainerConfig() {
super(DOCKER_IMAGE_VERSION);
}
@Override
public void stop() {
// do nothing, JVM handles shut down
}
}