본문 바로가기
자바 테스트

자바 테스트 격리

by byeongoo 2021. 1. 24.

이 포스팅은 우아한Tech의 보스독님의 테스트 격리 영상을 정리한 내용입니다.

 

■ 테스트 격리란?

테스트는 순서에 상관없이 독립적으로 실행되며 결정적으로 수행되어야합니다. 테스트를 서로 격리하여 한 테스트를 실행하여도 다른 테스트에 영향을 주지 않도록 해야합니다.

 

■ 계층별 테스트

데이터들이 공유되기 때문에 불완전한 테스트를 작성하게 됩니다. 따라서 데이터베이스를 얼마나 의존하지 않고 테스트를 작성할지 또는 데이터 베이스 상태를 테스트 이전으로 돌릴지에 대해서 신경을 써야합니다.

 

● Domain(POJO) 계층

  • 애플리케이션의 POJO(Model, Utils, etc...)는 JUnit으로 테스트
  • 객체는 new 연산자(또는 빌더)로 간단히 인스턴스화
  • 각각의 테스트가 실행되기 전에 @BeforeEach에서 인스턴스 초기화
  • 데이터베이스를 사용하지 않기 때문에 격리를 걱정할 필요 없음
private Question question;

@BeforeEach
void setUp(){
    question = Question.builder()
        .userId(1L)
        .title(TEST_QUETION_TITLE)
        .content(TEST_QUESTION_CONTENT)
        .build();
}

@DisplayName("조회수 초기값 확인")
@Test
void initValueOfVisits(){
    assertThat(question.getVisits().getVisitCount()).isEqualTo(0L);
}

@DisplayName("조회수 증가")
@Test
void increaseVisits(){
    question.increaseVisits();
    assertThat(question.getVisits().getVisitCount()).isEqualTo(1L);
}

 

● Service 계층

  • 실질적인 비즈니스 로직을 수행
  • 실제 데이터베이스 사용
  • 트랜잭션이 끝나면 데이터베이스 상태 변경
  • 테스트 간 격리가 필요
  • 실제 데이터베이스를 사용하면서 계층구조로 이루어져있기 때문에 사실상 통합 테스트가 되버림
  • @Transactional을 사용해서 테스트가 종료되면 rollback 가능
  • Mockito를 이용하면 실제 데이터베이스를 사용하지 않기 때문에 테스트 격리를 고민할 필요가 없다.

 

● Controller 계층

  • @SpringBootTest
    • Spring IoC로 실제 컨트롤러 빈을 사용해서 테스트
    • 실제 데이터베이스 사용
    • 통합 테스트
  • @WebMvcTest
    • MockMvc RestAPI 클라이언트 테스트 도구 사용
    • 데이터베이스를 사용하지 않고 단위 테스트 수행

● Repository 계층

  • @DataJpaTest
    • Slice Test 진행
    • In Memory로 테스트 수행
    • 자동으로@Transactional(rollback=true)이 사용됨

 

■ 인수 테스트

  • 시스템이 실제 운영 환경에서 사용될 준비가 되었는지 최종적으로 확인하는 단계
  • 실제 운영환경에 맞게 서버를 띄우고 데이터베이스를 사용
  • 테스트 격리를 신경쓰지  않으면 테스트가 실패하기 쉬움
  • 테스트 단위가 커서 한번 실패하면 디버깅하기 까다로움
  • Mock 객체가 아닌 실제 빈을 사용

 

인수 테스트 방법

1. @Transactional : 인수 테스트에서 제대로 작동하지 않음. 요청을 보내는 http client쪽과 실제 로직을 수행하는 서버  로직이 서로 다른 쓰레드에서 실행된다. 테스트 코드에서 어노테이션을 롤백 전략으로 해두어도, 다른 스레드에서 실행되는 서버 사이드 트랜잭션은 그 테스트 코드의 영향을 받지 않고 데이터베이스가 변하게 된다.

2. 매번 테스트 종료시 생성한 픽스처 및 데이터 삭제

3. 매번 테스트 종료시 테이블 Truncate

  • Delete보다 Truncate가 좋은 이유
    • 트랜잭션 로그 공간을 적게 차지
    • 쿼리 실행시 행단위로 락을 걸지 않음
  • 3-1 @Sql로 SQL 파일 실행

 

  • 3-2 EntityManager 이용 (보스독님이 추천하는 방식)

엔티티 매니저로 쿼리를 직접 만들어서 실행하는 방식. 엔티티에 있는 테이블 이름을 가지고온 후 리스트에 저장

데이터 베이스를 주입 받고 테스트를 실행하기 직전 @BeforeEach에서 테이블 이름을 조사한 후 Truncate를 실행. 이렇게 만들어 두면 추후 엔티티가 추가되거나 삭제될때 동적으로 테이블을 조사하기 때문에 테스트 격리에 투입되는 비용을 줄일 수 있음

 

■ 정리

  • 잘격리된 테스트는 유지보수가 수월
  • 더욱 안전한 테스트 작성으로 코드의 품질 보장