dependencies {
// MariaDB (test 전용) -> 우리는 일단 이것만 확인 할 예정.
testImplementation 'org.testcontainers:mariadb:1.17.6'
// Redis (test 전용)
testImplementation 'org.testcontainers:redis:1.17.6'
// Kafka (test 전용)
testImplementation "org.testcontainers:kafka:1.17.6"
}
@Testcontainers
@SpringBootTest
@ActiveProfiles("test")
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
public class IntegrationTestSupport {
// MariaDB 컨테이너 설정
@Container
static MariaDBContainer<?> MARIADB_CONTAINER = new MariaDBContainer<>("mariadb:10.11");
}
@Testcontainers
@SpringBootTest
@ActiveProfiles("test")
test
프로필을 활성화하는데 사용됩니다.@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
Replace.NONE
옵션을 통해 어떠한 자동 구성도 대체하지 않고, 테스트에서 지정한 MariaDB 컨테이너를 그대로 사용하게 한다.@Container
static MariaDBContainer<?> MARIADB_CONTAINER = new MariaDBContainer<>("mariadb:10.11");
mariadb:10.11
은 사용할 MariaDB의 버전을 지정한다. → 각 프로젝트 버전에 맞춰서 설명하면 됨.static
으로 둔 이유는 통합 테스트를 수행하는 테스트 클래스마다 컨테이너를 새로 실행하기 때문에 모든 테스트 메서드에서 컨테이너를 실행하는 비용을 줄일수 있었음.@Container
static MariaDBContainer<?> MARIADB_CONTAINER = new MariaDBContainer<>("mariadb:10.11")
.withInitScript("db/init.sql");
테스트 계층 | Testcontainers 사용 | 적합한 테스트 시나리오 | 대안적 테스트 방법 |
---|---|---|---|
Controller | 일반적으로 부적합 | N/A | @WebMvcTest, @MockMvc를 통한 HTTP 요청 및 응답 검증 |
Service | 조건적으로 적합 | 외부 시스템과의 복잡한 통합을 포함한 비즈니스 로직 검증 | @MockBean 사용하여 서비스 의존성 모의 |
Repository | 적합 | 실제 데이터베이스 인터랙션을 통한 데이터 접근 로직 검증 | @DataJpaTest 사용, 인-메모리 데이터베이스(H2) 활용 |
MockMvc
를 사용하여 컨트롤러의 요청 경로, 매개변수 처리, 응답 형식을 효과적으로 검증할 수 있다.장점:
단점:
@DataJpaTest
class AccountRepositoryTest extends IntegrationTestSupport {
@Autowired
private TestEntityManager entityManager;
@Autowired
private AccountRepository accountRepository;
@Test
public void test() {
// Given
Account account1 = new Account(null, "John Doe", 1000.00);
Account account2 = new Account(null, "John Doe", 500.00);
entityManager.persist(account1);
entityManager.persist(account2);
entityManager.flush();
// When
List<Account> accounts = accountRepository.findByOwnerAndBalanceGreaterThan("John Doe", 750);
// Then
assertThat(accounts).hasSize(1);
assertThat(accounts.get(0).getBalance()).isEqualTo(1000.00);
}
}
IntegrationTestSupport
를 상속받아 사용하였다.@SpringBootTest
로 충분할 수 있다.public class AccountControllerIntegrationTest extends IntegrationTestSupport {
@Autowired
private TestRestTemplate restTemplate;
private String getBaseUri() {
return "http://localhost:" + port + "/api/accounts";
}
@Test
void testCreateAccount() {
// 계정 생성 요청
Account account = Account.builder()
.owner("John Doe")
.balance(1000.0)
.build();
ResponseEntity<Account> response = restTemplate.postForEntity(getBaseUri(), account, Account.class);
// 결과 검증
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
assertThat(response.getBody().getOwner()).isEqualTo("John Doe");
assertThat(response.getBody().getBalance()).isEqualTo(1000.0);
}
@Test
void testGetAccountById() {
// 계정 생성 및 ID 획득
Account account = Account.builder()
.owner("Jane Doe")
.balance(500.0)
.build();
Account savedAccount = restTemplate.postForObject(getBaseUri(), account, Account.class);
// 생성된 계정 ID로 조회
ResponseEntity<Account> response = restTemplate.getForEntity(getBaseUri() + "/" + savedAccount.getId(), Account.class);
// 결과 검증
assertThat(response.getStatusCode()).isEqualTo(HttpStatus.OK);
assertThat(response.getBody()).isNotNull();
assertThat(response.getBody().getId()).isEqualTo(savedAccount.getId());
assertThat(response.getBody().getOwner()).isEqualTo("Jane Doe");
}
}
IntegrationTestSupport
를 상속받아서 사용하고 있다.