테스트 실행 시간 줄이기

 

  • 인수테스트시 DirtiesContext로 테스트 격리를 해주고 있다.
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
  • 273테스트 1min 1sec

 

오래 걸리는 테스트시간을 줄일 수 있는 방법이 없을 까 고민하던 중 좋은 블로그글을 발견했다..! DirtiesContext를 사용하지 않고 테스트 격리를 해서 매번 모든 Context가 새로 생성되는 비효율성을 줄일 수 있다. 테스트에서는 공유자원으로 사용되는 데이터베이스에 대한 격리가 이루어져야 하므로, TRUNCATE를 통해 데이터베이스 초기화를 시켜주는 방법으로 테스트 격리를 하는 것이다.

 

 

 

https://newwisdom.tistory.com/95

 

@DirtiesContext로 무거워진 인수 테스트 시간을 줄이는 실험을 해봅시다

인수 테스트 - @DirtiesContext 를 제거하자! 각각의 ATDD 테스트는 테스트 간 격리성을 가져야 하는데, RestAssured 테스트는 트랜잭션을 사용하지 못한다. 즉 데이터 관련 컨텍스트가 공유되고, 이 데이

newwisdom.tistory.com

  • @DirtiesContext제거 하고, 인수테스트 수행 하기 전 DB를 초기화 하도록 변경하니 11sec 387ms로 테스트 수행시간이 현저히 감소했다!

 

DirtiesContext를 제거하면서 공부했던 내용을 적어본다.

 

DirtiesContext

  • 테스트 수행 전, 수행 후, 각 테스트 케이스를 수행하기 전, 수행한 후에 Context를 재생성하는 역할을 해준다.
  • 애플리케이션 컨텍스트가 변경되었을 때 기존 애플리케이션 컨텍스트를 폐기하고, 새로운 애플리케이션 컨텍스르를 생성해서 사용할 수 있도록 한다.

 

ApplicationContext

  • Spring에서 관리하는 빈들이 담겨있는 컨테이너
  • Spring에서는 빈의 생성과 관계설정 같은 제어를 담당하는 IoC컨테이너인 빈 팩토리가 존재한다.
  • 애플리케이션 컨텍스트는 별도의 정보를 참고해서 빈의 생성, 관계설정 등의 제어 작업을 총괄한다.

 

테스트 격리

  • 테스트들은 서로 순서에 상관없이 독립적으로 수행되어야 한다.
  • 테스트 격리가 안 되는 근본적인 원인은 각각의 테스트가 하나의 자원을 공유하기 때문이다.
    • 공유자원의 대표주자 '데이터베이스'
  • 인수테스트는 실제 운영 환경과 같은 조건에서 테스트 되어야 하므로 '데이터베이스'를 사용해야한다.
  • 테스트가 진행됨에 따라 데이터베이스를 건드리게 된다.

 

@Transactional

  • 테스트에서 @Transactional을 통해 롤백하는 전략은 인수테스트에서 사용 불가능하다.
  • 인수테스트로 HTTP 요청을 보내는 부분(Client)과, 인수테스트를 위해 서버를 띄운 부분(Server)이 다른 쓰레드이기 때문이다.

 

EntityManager로 직접 TRUNCATE 쿼리 실행

 

  • 테스트 환경에서만 빈이 등록되도록 설정해주었다.
  • 빈등록 시 테이블 이름을 뽑아내고, cleanUp 호출 시 해당 테이블을 TRUNCATE하는 작업을 수행한다.
@Component
@Profile("test")
public class DatabaseCleanup implements InitializingBean {

    private final EntityManager entityManager;

    private List<String> tableNames;

    public DatabaseCleanup(EntityManager entityManager) {
        this.entityManager = entityManager;
    }


    @Override
    public void afterPropertiesSet() throws Exception {
        tableNames = entityManager.getMetamodel().getEntities().stream()
                .filter(e -> e.getJavaType().getAnnotation(Entity.class) != null)
                .map(e -> CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_UNDERSCORE, e.getName()))
                .collect(Collectors.toList());
    }

    @Transactional
    public void cleanUp() {
        entityManager.flush();
        entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY FALSE").executeUpdate();

        for (String tableName: tableNames) {
            entityManager.createNativeQuery("TRUNCATE  TABLE " + tableName).executeUpdate();
            entityManager.createNativeQuery("ALTER  TABLE " + tableName +
                    " ALTER  COLUMN  ID RESTART  WITH  1").executeUpdate();
        }

        entityManager.createNativeQuery("SET REFERENTIAL_INTEGRITY TRUE").executeUpdate();
    }
}

 

  • 인수테스트의 setUp에서 데이터 cleanUp을 수행해준다.
@BeforeEach
protected void setUp() {
    RestAssured.port = port;
    databaseCleanup.cleanUp();
        
 }

 

 

https://tecoble.techcourse.co.kr/post/2020-09-15-test-isolation/

 

인수테스트에서 테스트 격리하기

tecoble.techcourse.co.kr

 

InitialzingBean

  • 스프링 빈이 생성될 때 호출된다.
    • afterPropertiesSet 메서드

 

 

'우아한테크코스' 카테고리의 다른 글

우아한테크코스 글쓰기 아카이브  (0) 2021.11.20
[테코톡]TCP/IP  (6) 2021.11.09
[팀프로젝트] 5,6주차  (0) 2021.08.12
배포인프라 수업 다시보기  (0) 2021.08.04
[팀프로젝트]4주차  (0) 2021.07.27

댓글



Designed by JB FACTORY