항해99

항해99 45일차 TIL1 - 테스트 환경 데이터베이스 만들기

자몽포도 2023. 1. 30. 17:08

트러블 슈팅이라고 하기에는 조금 애매하지만 현재 프로젝트는 배포시마다 데이터베이스가 초기화되는 문제가 있습니다.

 

그 원인은 아래와 같습니다.

 

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

 

그 결과 데이터베이스에 변경 내역이 존재하지 않게 됩니다.