테스트 데이터베이스로 기본 내장된 h2를 사용하지 않고 Testcontainers를 사용하였다. 이는 테스트코드의 멱등성을 유지하고 테스트의 신뢰성과 정확성을 높이기 위함이다.
TestContainers를 통해 운영환경을 동일한 조건으로 맞추어 테스트를 수행할 수 있었다.
( 실제로 h2를 활용해 테스트 데이터베이스를 생성하여 진행한 결과 운영환경의 실제 DB인 postgreSql의 jsonb타입을 처리하기가 까다로웠다. 또한, 테스트는 통과했는데 운영 서버에서는 쿼리 오류가 나는 상황을 피하고자 하였다.)
테스트 클래스마다 컨테이너를 생성하면 테스트 속도가 매우 느려지기 때문에 AbstractIntegrationTest에서 컨테이너를 static으로 선언하여 전체 테스트 세션 동안 하나의 컨테이너를 공유하도록 설계하였다.
@Testcontainers
@ActiveProfiles("test")
public abstract class AbstractIntegrationTest {
// static으로 선언하여 전체 테스트 세션에서 컨테이너 공유
@Container
protected static final PostgreSQLContainer<?> postgresContainer =
new PostgreSQLContainer<>("postgres:15-alpine")
.withDatabaseName("testdb")
.withUsername("test")
.withPassword("test");
@DynamicPropertySource
static void setDatasourceProperties(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.jpa.hibernate.ddl-auto", () -> "create-drop");
}
}
@DataJpaTest를 사용한 Repository Layer 테스트 시에는 JPA 관련 빈들만을 스캔한다.
해당 프로젝트에서는 QueryDsl을 활용하여 DB작업이 이루어지는 부분이 있기 때문에 ProductCustomRepositoryImpl 클래스에 JPAQueryFactory가 정의되어있다. 따라서, 이와 관련한 QueryDsl 설정파일은 따로 import 하여 수동으로 주입시켜줘야 오류가 나지 않는다.
@TestConfiguration
public class QueryDslTestConfig {
@PersistenceContext
private EntityManager entityManager;
@Bean
public JPAQueryFactory jpaQueryFactory() {
return new JPAQueryFactory(entityManager);
}
}
@AutoConfigureTestDatabase(replace = NONE) 설정을 해줌으로써 DataJpaTest가 기본으로 내장되어있는 h2를 사용하지 않고 DynamicPropertySource로 정의한 TestContainers의 postgreSql을 쓸 수 있게된다.
@DataJpaTest
@Import(QueryDslTestConfig.class)
@AutoConfigureTestDatabase(replace = NONE)
public class RepositorySliceTest extends AbstractIntegrationTest {
}
아래와 같이 적용하여 테스트 코드를 작성해주었다.
@Transactional
@DisplayName("UserRepository 슬라이스 테스트")
class UserRepositoryTest extends RepositorySliceTest {
@Autowired
private UserRepository userRepository;
@Test
@DisplayName("T1-(1). findByEmail 성공 테스트 - 이메일을 이용한 사용자 조회")
void findByEmail_Success(){
User user = User.builder()
.email("test@test.com")
.password("password")
.roles(Set.of(Role.ROLE_SELLER,Role.ROLE_BUYER))
.build();
userRepository.save(user);
Optional<User> result = userRepository.findByEmail("test@test.com");
assertThat(result).isPresent();
assertThat(result.get().getEmail()).isEqualTo("test@test.com");
}
}
.
.
.
다음 포스팅에서는 통합테스트를 하는 방법과 그 예시에 대해 정리한다.