H2 대신에 mysql testcontainer를 사용하여 테스트 코드 작성
1. 개요
Spring Data JPA는 내장 된 H2 데이터베이스로 데이터베이스 쿼리를 생성하고 테스트하는 쉬운 방법을 제공합니다.
그러나 실제 서비스에서는 h2가아닌 mysql등에에 대한 쿼리를 사용합니다. 이렇게 되면 실제 사용하는 mysql과 테스트하는 h2와 괴리감이 발생합니다. 그렇기 때문에 실제 테스트를할 때도 mysql과 동일환 환경에서 테스트를 하는것이 중요합니다. 이 튜토리얼에서는 Spring Data JPA 및 PostgreSQL 데이터베이스와의 통합 테스트 를 위해 Testcontainers 를 사용하는 방법을 보여 줍니다 .
이전 자습서에서는 주로 @Query annotation을 사용하여 데이터베이스 쿼리를 만들었습니다 .
2. 구성
우리의 테스트에서 PostgreSQL 데이터베이스를 사용하려면, 우리는 추가해야 Testcontainers 의존성 과 시험 범위 와 PostgreSQL의 드라이버 우리에 pom.xml 파일을 :
<dependency>
<groupId>org.testcontainers</groupId>
<artifactId>postgresql</artifactId>
<version>1.10.6</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<version>42.2.5</version>
</dependency>
Spring이 적절한 드라이버 클래스를 사용하고 각 테스트 실행에서 스키마를 작성 및 삭제하도록 지시하는 테스트 자원 디렉토리 아래에 application.properties 파일을 작성하자 .
spring.datasource.driver-class-name=org.postgresql.Driver
spring.jpa.hibernate.ddl-auto=create-drop
3. 단일 테스트 사용법
단일 테스트 클래스에서 PostgreSQL 인스턴스 사용을 시작하려면 먼저 컨테이너 정의를 작성한 다음 해당 매개 변수를 사용하여 연결을 설정해야합니다.
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(initializers = {UserRepositoryTCIntegrationTest.Initializer.class})
public class UserRepositoryTCIntegrationTest extends UserRepositoryCommonIntegrationTests {
@ClassRule
public static PostgreSQLContainer postgreSQLContainer = new PostgreSQLContainer("postgres:11.1")
.withDatabaseName("integration-tests-db")
.withUsername("sa")
.withPassword("sa");
static class Initializer
implements ApplicationContextInitializer<ConfigurableApplicationContext> {
public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
TestPropertyValues.of(
"spring.datasource.url=" + postgreSQLContainer.getJdbcUrl(),
"spring.datasource.username=" + postgreSQLContainer.getUsername(),
"spring.datasource.password=" + postgreSQLContainer.getPassword()
).applyTo(configurableApplicationContext.getEnvironment());
}
}
}
위의 예제 에서 JUnit의 @ClassRule 을 사용 하여 테스트 메소드를 실행하기 전에 데이터베이스 컨테이너를 설정했습니다 . ApplicationContextInitializer 를 구현하는 정적 내부 클래스도 만들었습니다 . 마지막 단계로, 초기화 클래스를 매개 변수로 사용하여 @ContextConfiguration 주석을 테스트 클래스에 적용했습니다 .
이 세 가지 동작을 수행하면 Spring 컨텍스트가 게시되기 전에 연결 속성을 설정할 수 있습니다.
이전 기사에서 두 개의 UPDATE 쿼리를 사용하자 :
@Modifying
@Query("update User u set u.status = :status where u.name = :name")
int updateUserSetStatusForName(@Param("status") Integer status,
@Param("name") String name);
@Modifying
@Query(value = "UPDATE Users u SET u.status = ? WHERE u.name = ?",
nativeQuery = true)
int updateUserSetStatusForNameNative(Integer status, String name);
다음과 같이 테스트 가능합니다.
@Test
@Transactional
public void givenUsersInDB_WhenUpdateStatusForNameModifyingQueryAnnotationJPQL_ThenModifyMatchingUsers(){
insertUsers();
int updatedUsersSize = userRepository.updateUserSetStatusForName(0, "SAMPLE");
assertThat(updatedUsersSize).isEqualTo(2);
}
@Test
@Transactional
public void givenUsersInDB_WhenUpdateStatusForNameModifyingQueryAnnotationNative_ThenModifyMatchingUsers(){
insertUsers();
int updatedUsersSize = userRepository.updateUserSetStatusForNameNative(0, "SAMPLE");
assertThat(updatedUsersSize).isEqualTo(2);
}
private void insertUsers() {
userRepository.save(new User("SAMPLE", "email@example.com", 1));
userRepository.save(new User("SAMPLE1", "email2@example.com", 1));
userRepository.save(new User("SAMPLE", "email3@example.com", 1));
userRepository.save(new User("SAMPLE3", "email4@example.com", 1));
userRepository.flush();
}
위 시나리오에서 첫 번째 테스트는 성공하지만 두 번째 테스트는 메시지와 함께 InvalidDataAccessResourceUsageException 을 발생시킵니다.
Caused by: org.postgresql.util.PSQLException: ERROR: column "u" of relation "users" does not exist
H2 내장 데이터베이스를 사용하여 동일한 테스트를 실행하면 두 테스트가 모두 성공적으로 완료되지만 PostgreSQL은 SET 절에서 별칭을 허용하지 않습니다. 문제가있는 별칭을 제거하여 쿼리를 빠르게 수정할 수 있습니다.
@Modifying
@Query(value = "UPDATE Users u SET status = ? WHERE u.name = ?",
nativeQuery = true)
int updateUserSetStatusForNameNative(Integer status, String name);
이번에는 두 테스트가 모두 성공적으로 완료되었습니다. 이 예에서는 테스트 컨테이너를 사용하여 프로덕션에서 실제 데이터베이스로 전환 한 후 공개 될 수있는 기본 쿼리의 문제점을 식별했습니다. 또한 Spring은 사용 된 데이터베이스 공급자에 따라 올바르게 변환하기 때문에 JPQL 쿼리 를 사용 하는 것이 일반적으로 더 안전 하다는 점에 유의해야합니다 .
4. 공유 데이터베이스 인스턴스
이전 단락에서는 단일 테스트에서 Testcontainers를 사용하는 방법에 대해 설명했습니다. 실제 시나리오에서는 시작 시간이 비교적 길기 때문에 여러 테스트에서 동일한 데이터베이스 컨테이너를 재사용하고 싶습니다.
PostgreSQLContainer 를 확장 하고 start () 및 stop () 메소드를 재정 의하여 데이터베이스 컨테이너 작성을위한 공통 클래스를 작성해 보겠습니다 .
public class BaeldungPostgresqlContainer extends PostgreSQLContainer<BaeldungPostgresqlContainer> {
private static final String IMAGE_VERSION = "postgres:11.1";
private static BaeldungPostgresqlContainer container;
private BaeldungPostgresqlContainer() {
super(IMAGE_VERSION);
}
public static BaeldungPostgresqlContainer getInstance() {
if (container == null) {
container = new BaeldungPostgresqlContainer();
}
return container;
}
@Override
public void start() {
super.start();
System.setProperty("DB_URL", container.getJdbcUrl());
System.setProperty("DB_USERNAME", container.getUsername());
System.setProperty("DB_PASSWORD", container.getPassword());
}
@Override
public void stop() {
//do nothing, JVM handles shut down
}
}
이탈하여 정지 () 비어있는 방법을, 우리는 JVM이 컨테이너 종료를 처리 할 수 있습니다. 또한 첫 번째 테스트 만 컨테이너 시작을 트리거하고 이후의 각 테스트에서 기존 인스턴스를 사용하는 간단한 싱글 톤 패턴을 구현합니다. 에서 시작 () 방법 우리가 사용하는 시스템 # setProperty는를 환경 변수로 설정 연결 매개 변수에.
이제 application.properties 파일 에 넣을 수 있습니다.
spring.datasource.url=${DB_URL}
spring.datasource.username=${DB_USERNAME}
spring.datasource.password=${DB_PASSWORD}
테스트 정의에서 유틸리티 클래스를 사용하자 :
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserRepositoryTCAutoIntegrationTest {
@ClassRule
public static PostgreSQLContainer postgreSQLContainer = BaeldungPostgresqlContainer.getInstance();
// tests
}
이전 예제에서와 같이 @ClassRule 주석을 컨테이너 정의를 보유하는 필드에 적용 했습니다 . 이런 식으로 스프링 컨텍스트 생성 전에 DataSource 연결 속성이 올바른 값으로 채워집니다.
BaeldungPostgresqlContainer 유틸리티 클래스로 인스턴스화 된 @ClassRule 어노테이션이있는 필드 를 정의하기 만하면 동일한 데이터베이스 인스턴스 를 사용하여 여러 테스트를 구현할 수 있습니다 .
5. 결론
이 기사에서는 Testcontainers를 사용하여 실제 데이터베이스 인스턴스에서 테스트를 수행하는 방법을 설명했습니다.
Spring 의 ApplicationContextInitializer 메커니즘을 사용하고 재사용 가능한 데이터베이스 인스턴스화를위한 클래스를 구현하는 단일 테스트 사용법의 예를 살펴 보았다 .
또한 Testcontainers가 여러 데이터베이스 공급자, 특히 기본 쿼리의 호환성 문제를 식별하는 데 어떻게 도움이되는지 보여주었습니다. 완전한 코드는 GitHub에서 사용할 수 있습니다 .
참고
https://www.baeldung.com/spring-boot-testcontainers-integration-test