Unit Testing 10장 : 데이터베이스 테스트

    들어가기 전

    이 글은 단위 테스트 10장 데이터베이스 테스트를 공부하며 작성한 글입니다.


    10. 데이터베이스 테스트

    통합 테스트에서 중요한 부분 중 하나는 데이터베이스 테스트다. 실제 데이터베이스를 테스트하면 회귀 방지가 아주 뛰어나지만 유지 보수 및 설정하기가 쉽지 않다. 이 장에서는 데이터베이스 테스트를 잘 하는 방법, 어디까지 해야하는지를 알아보고자 한다.


    10.2.2 다른 컨텍스트에서 테스트 하기

    데이터베이스를 테스트 할 때는 실제 운영환경에 가깝게 테스트를 해야하는 것이다. 즉, 테스트 코드를 작성할 때 입력과 실행 환경의 컨텍스트를 서로 다르게 가져가서 테스트 해야한다는 것이다. 예를 들어 JPA를 들어보자.

    JPA는 영속성 컨텍스트가 있다. 이것을 바탕으로 고민해보자.

    1. 실행 전, DB에 입력을 집어넣어두면 영속성 컨텍스트에 객체가 보관된다.
    2. 실행하면 DB에서 새로운 값을 불러오기 전에 영속성 컨텍스트에 값이 있는지 확인한다. 

    이 두 단계 때문에 문제가 발생한다. 1과 2는 같은 컨텍스트에서 동작한다. 운영 환경은 '메서드를 실행했을 때' 새로운 객체를 데이터베이스에서 불러온다. 그런데 위와 같이 테스트를 하면 입력에서 사용했던 컨텍스트를 그대로 사용했기 때문에 운영 환경과 서로 다르게 동작하는 것이다. 따라서 테스트 코드에서 입력 / 실행 사이에 다음 코드를 넣을 것을 권장한다.

    EntityManager em;
    
    @Test
    void test1(){
    
    	em.flush();
        em.clear();
        
    }

     


    10.3 테스트 데이터 생명 주기

    테스트는 데이터베이스에 따라 다른 결과를 나타내면 안된다. 테스트는 데이터베이스를 '테스트가 원하는 상태'로 만들어야 하고, 이를 위해서 아래 원칙을 따르도록 한다. 

    • 모든 테스트는 순차적으로 실행한다. 
    • 테스트 실행 전에 남은 데이터를 삭제해라. 

    10.3.1 병렬 / 순차적 테스트 실행

    병렬로 테스트를 수행하면 테스트 수행 시간이 단축된다. 하지만 병렬로 데이터베이스를 테스트 하기 위해서 필요한 유지보수 비용이 너무 크다. 비용 편익을 생각하면 병렬로 테스트 하는 것보다 시간이 조금 더 걸리더라도 순차적으로 데이터베이스를 테스트 하는 것이 좋다. 


    10.3.2 테스트 실행 간 데이터 정리

    테스트 사이에 데이터를 정리하는 방법은 여러 가지가 있는데, 권장하는 방법은 테스트를 시작하는 시점에 모든 데이터를 삭제하고 시작하는 것이다. 데이터를 삭제하는 것은 공통 클래스, 메서드로 추출한 다음에 @BeforeEach() 같은 것들로 매번 실행되도록 할 수 있다.

    이 외에 테스트 사이에 데이터를 정리하는 방법은 다음이 있을 수 있다.

    • 각 테스트 전에 데이터베이스 백업 복원하기 
      • DB를 백업하고 복원하기 때문에 수행 시간이 길어진다.
    • 테스트 종료 시점에 데이터 정리하기
      • 까먹을 수도 있다.
    • 데이터베이스 트랜잭션에 각 테스트를 래핑하고 커밋하지 않기
      • 추가 트랜잭션을 사용하기 때문에 운영 환경과 다른 설정이 생성된다. 
    • 테스트 시작 시점에 데이터 정리하기
      • 빠르게 작동하고 일관성이 없는 동작을 일크지 않고, 정리 단계를 실수로 건너뛰지 않는다.

    10.3.3 인메모리 데이터베이스 피하기

    테스트에서 데이터베이스 간 격리를 위해서 SQLite, H2 같은 인메모리 데이터베이스를 사용할 수도 있다. 그렇지만 인메모리 데이터베이스를 사용하는 것은 멀리해야한다. 인메모리 DB 사용의 장단점은 다음과 같다.

    • 장점
      • 테스트 데이터를 제거할 필요가 없음.
      • 작업 속도 향상
      • 테스트가 실행될 때 마다 인스턴스화 가능
    • 단점
      • 실제 사용하는 데이터베이스와 기능적으로 일관성이 없음.
      • 운영환경과 일치하지 않음. 

    10.4 테스트 구절에서 코드 재사용하기 

    통합 테스트는 가능한 짧게 하되 가독성에 영향을 주지 않는 것이 중요하다. 통합테스트를 짧게 하는 좋은 방법 중 하나는 비즈니스 로직과 관련이 없는 부분을 메서드나 클래스로 추출하고 재사용해서 사용하는 것이다. 

    • Given : 필요한 객체들을 공급하는 행위를 메서드나 클래스로 추출한다. 이 때 기본값을 활용할 수도 있다.
    • When : 트랜잭션 같이 Try ~ Catch가 필요한 부분을 AOP 형태의 메서드를 작성해서 가독성을 증가시킬 수 있다.
    • Then : 검증 구절 중 함께 자주 사용되는 것이 있다면, 묶어서 비공개 메서드로 추출한다.

    10.5 데이터베이스 테스트에 대한 일반적인 질문

    이 장에서는 데이터베이스와 관련된 테스트 상황을 공부해보고자 한다.


    10.5.1 읽기 테스트를 해야하는가?

    가장 복잡하거나 중요한 읽기만 테스트하고 나머지는 테스트 하지 않는 것이 좋다.

    쓰기를 철저히 테스트 하는 것은 매우 중요하다. 쓰기 작업이 잘못되면 데이터가 손상되고, DB 뿐만 아니라 외부 어플리케이션에도 영향을 미칠 수 있다. 쓰기와 관련된 테스트는 이런 부분에 있어 회귀 방지를 제공할 수 있기 때문에 중요하다. 반면 읽기는 DB에 영향을 주지 않기 때문에 쓰기에 비해 해로운 문제가 많지는 않다. 따라서 복잡하거나 중요한 읽기만 테스트하고 나머지는 테스트 하지 않는 쪽으로 접근한다. 


    10.5.2 리포지토리 테스트를 해야하는가?

    리포지토리는 DB와의 접근 방식을 추상화한 계층이다. 이 리포지토리를 통합 테스트와는 독립적으로 테스트 해야할까? 결론부터 이야기하면 리포지토리는 일반적인 경우에는 테스트 하지 않는 것을 추천한다. 만약 리포지토리에 복잡한 로직(엔티티 맵핑 / 슬라이싱 / 페이징)등이 있다면, 이것을 분리하고 이 메서드만 테스트 하는 것을 추천한다. 이유는 비용 편익 대비 테스트 효과가 좋지 않기 때문이다. 아래에서 자세한 내용을 살펴본다. 

    높은 유지비

    리포지토리는 관리 의존성인 DB와 통신을 한다. 따라서 컨트롤러 영역에 포함된다. 그리고 일반적으로 프로세스 외부 의존성이 추가되면 유지보수 비용이 증가한다. DB는 비관리 의존성이지만, 외부 프로세스 의존성이기 때문에 유지보수 비용이 필요하다. 즉, 리포지토리를 테스트 하는 것은 통합 테스트를 꾸리는 것만큼의 유지보수 비용이 발생하지만 테스트 효과는 미미하다. 

     

    낮은 회귀 방지

    리포지토리는 복잡하지 않으며 실행되는 코드의 양도 많지 않다. 따라서 회귀 방지에 있어서도 좋은 이점을 제공하지 못한다. 만약 리포지토리에서 복잡한 로직(페이징, 슬라이싱, 엔티티 맵핑)이 있다면 그것을 따로 분리한 후에 이것들을 테스트 하는 것을 추천한다. 실제 DB와 통신을 하는 것은 외부 프로세스가 추가 되는 것이고 이에 대해서 유지 비용이 발생하기 때문이다. 


    요약

    • 준비 / 실행 / 검증 구절에 각각 고유의 트랜잭션이 있어야 한다. 이것은 운영환경에 가깝게 맞추어야 하기 때문이다.
    • 통합 테스트는 순차적으로 실행해라. 병렬 실행에는 상당한 노력이 필요하며, 보통 그럴 가치가 없다.
    • 테스트 시작 시점에 남은 데이터를 정리하라. 
    • 인메모리 DB를 테스트 용으로 사용하지 말라. 다른 업체의 데이터베이스로 테스트를 실행하면 회귀 방지 및 보호 수준이 떨어지기 때문이다.
    • 테스트에서 비공개 메서드 / 헬퍼 클래스를 이용해 테스트 코드를 단축할 수 있다.
      • Given : 테스트 데이터 빌더를 사용
      • When : 데코레이터 메서드 (트랜잭션 AOP 같은)
      • Then : 플루언트 인터페이스 
    • 데이터 베이스의 읽기 테스트 코드는 꼭 필요하고 복잡한 로직만 한다. 왜냐하면 버그를 불러일으킬 가능성이 쓰기에 비해 낮기 때문이다.
    • 리포지토리는 직접 테스트 하지 말고 통합 테스트 스위트로 취급하라. 리포지토리 테스트는 회귀 방지에 대한 이득이 너무 적은데, 유지비가 높다. 

    댓글

    Designed by JB FACTORY