항해99 45일차 TIL1 - 테스트 환경 데이터베이스 만들기
트러블 슈팅이라고 하기에는 조금 애매하지만 현재 프로젝트는 배포시마다 데이터베이스가 초기화되는 문제가 있습니다.
그 원인은 아래와 같습니다.
1. 각각의 테스트 전에 테이블을 초기화합니다.
@BeforeEach
void beforeEach() {
userRepository.deleteAll();
}
2. 로컬에서는 application.properties 를 조작하여
H2를 사용하지만 배포될 때는 DB가 운영 서버에서 사용하는 디비로 연동이 됩니다.
문제를 해결해보겠습니다.
테스트 서버 설정 분리
test 패키지 내부에 resources/application.properties를 만듭니다. 이렇게되면 테스트 실행 시, main에 있는 application.properties 보다 우선권을 가지게 됩니다. 즉, test/resources/application.properties 의 설정대로 테스트가 동작합니다.
test/resources/application.properties
spring.profiles.active=test // 여기선 큰 의미 없습니다.
spring.datasource.url=jdbc:h2:mem:db
spring.datasource.username=sa
spring.datasource.password=
//... 이하 설정 내용
이렇게 되면 테스트 서버와 개발 및 운영 서버의 환경이 분리되었습니다. 그리고 개발 및 운영 서버의 데이터베이스도 더 이상 초기화 되지 않습니다.
아직 모자라다... 트랜잭션, 롤백 활용
제가 듣고 있는 강의에 따르면 테스트에서 중요한 원칙 2가지가 있습니다.
- 테스트는 다른 테스트와 격리해야 한다.
- 테스트는 반복해서 실행할 수 있어야 한다.
이 원칙에 따르면 각각의 테스트를 수행할 때 데이터베이스가 초기화 되어야 합니다. 그래서 아래와 같은 코드를 짜기도 합니다.
@BeforeEach
void beforeEach() {
userRepository.deleteAll();
}
하지만 이는 완벽하지 않습니다. 왜냐하면 테스트를 수행하다 애플리케이션이 종료되면 데이터베이스에 데이터가 남아있을수도 있기 때문입니다.
이 때 활용할 수 있는 것이 트랜잭션입니다. 테스트 클래스에 시작마다 커낵션을 얻고 마칠 때마다 롤백을 해줍시다.
@Autowired
PlatformTransactionManager transactionManager;
TransactionStatus status;
@BeforeEach
void beforeEach() {
status = transactionManager.getTransaction(new DefaultTransactionDefinition());
}
@AfterEach
void afterEach() {
transactionManager.rollback(status);
}
이를 더 쉽게 사용하려면 클래스단에 @Transcational을 사용하면 됩니다. 원래 @Transcational 애노테이션은 로직 성공 유무에 따라 커밋, 롤백을 대신 수행해줍니다. 하지만 테스트 환경에서는 @Transcational이 조금 다른 기능을 가지고 있습니다.
1. 오토 커밋을 false
2. 테스트 수행(SQL 날라감)
3. 테스트 종료
4. Rollback
그 결과 데이터베이스에 변경 내역이 존재하지 않게 됩니다.