JUnit Test에서 H2 DB에서 MySQL DB로 바꾼 썰

테스트용 DB를 쓰는 이유

스프링에서는 일반적으로 테스트를 할 때 Test용 DB를 별도로 사용한다.

우리팀의 경우 h2 인메모리 DB를 테스트시 사용했었다.

그러나 모종의 이유로 개발/프로덕션용 DB인 MySQL DB를 테스트용으로 사용해야할 필요성이 생겼다.

H2 DB를 MySQL로 바꾸면서 테스트들이 깨지기 시작했는데 그 이유에 대해서 알아보려 한다.

H2 DB란

우선 이 내용을 이해하기 위해 테스트 DB로 사용한 H2 DB에 대해서 알아야한다.

H2 DB는 자바 기반의 오픈소스 RDBMS이다.

특이한 점은 인메모리 DB 기능을 지원한다는 점이다.

그래서 보통 테스트시에 주로 사용하는 DB이다.

테스트가 깨진 이유

테스트가 깨진 이유는 간단했다.

우리는 @SQL 애노테이션을 사용해서 sql 스크립트를 테스트가 시작할 때 DB에 적재해두고 @Transactional 애노테이션을 사용해서 각각의 단위 테스트가 종료되면 롤백시킨 후 다시 스크립트를 인서트 시키려고 했다.

이런 방법을 사용한 이유는 모든 단위테스트가 독립적인 환경에서 테스트되기를 바랬기 때문이다.

문제는 H2 DB의 경우 @Transational 애노테이션을 사용하여 롤백시켜도 메모리에서 관리가 되기때문에 크게 상관이 없었다.

그러나 이미 DB 서버에 배포된 DB에 롤백이 되지 않았고매 단위테스트마다 @Sql 애노테이션에 정의된 스크립트를 DB에 인서트하려고 해서 에러가 발생했다.

그래서 이 문제로 꽤나 골머리를 앓았다.

이를 해결하기 위해서는 두 가지 방법이 있다.

1. @DirtiesContext

매번 스프링 애플리케이션을 실행시키면서 단위테스트를 동작하게 하는 상당히 띠용한 기능이다.

이 기능을 사용하면 간단하게 Rollback(이라고 부르기도 뭐한..)을 구현할 수 있지만,

매번 애플리케이션이 단위 테스트때마다 실행되기 때문에 속도도 느리고, 자칫 CI/CD 서버에서 부하를 일으킬 수도 있기 때문에 좋지않은 방법이다. (라고 팀의 시니어분이 알려주셨다.)

또한 DB 커넥션을 종료하지않고 애플리케이션이 새로 뜨기 때문에 운영중인 DB에서 혹시나 커넥션이 제대로 종료되지 않은 경우가 생기면 DB 성능상에 문제가 발생할 수도 있기 때문에 좋지 않은 방법이기도 하다.

그래도 혹시나 어떤 이유로 사용하실 분들이 계실 수 있어서 사용법은 아래에 남겨놓도록 하겠다.

@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)

2. entityManager의 사용

간단한 방법인데 JUnit5의 @AfterEach 애노테이션을 사용한 함수 내부에서 entityManager를 사용해 DB Row들을 모두 지우고 flush() 메소드를 사용해 함수가 종료될 때 DB에 반영시켜버리는 방법이 있다.

이렇게 하면 데이터 Insert -> 단위 테스트 동작 -> 데이터 모두 제거 라는 프로세스로 진행이 되면서 모든 단위테스트가 동일한 환경에서 실행될 수 있다.

역시나 단점으로는 매번 DB row를 지우는 작업이 있기 때문에 속도가 느릴 수 있고 딱 보면 알겠지만 그리 좋은 테스트 방법이 아님을 알 수 있다.

결론

현재는 2번의 방법을 사용해서 테스트를 독립적인 환경으로 만들었다.

팀의 시니어분께서도 테스트에 너무 많은 시간을 쏟을 필요 없다. 돌아가게 만들고 나중에 해도 괜찮다라는 이야기를 들어서 너무 여기에 공을 쏟는게 아닌가 싶은 생각이 들었다.

사실 테스트 코드는 반드시 있어야하고 테스트 코드를 잘 짜는 것은 중요하지만 기능을 구현하고 성능을 최적화하는게 우선인 상황에서 테스트 코드에 시간을 너무 많이 쏟는 내 자신이 미웠다.

기능 구현과 성능 최적화를 하고 나서 다시 한번 테스트 코드를 재정비할 생각이다.

그래서 현재로써는 2번의 방법을 사용하고 있고 혹시나 이 글을 보고 더 좋은 방법을 알고 계시다면 꼭 댓글로 알려주시길 바라며 이 글을 마치도록 하겠다.



© 2022. by minkuk

Powered by minkuk