[우아한테크코스] 10월 6일 TIL

less than 1 minute read

[Spring] 인수테스트 격리하기

  1. @Transactional 적용

    @SpringBootTest 어노테이션에 포트번호를 지정해 서버를 띄우는데 이때 http client와 서버의 로직이 서로 다른 thread에서 실행돼 롤백이 걸려도 다른 스레드에서 실행되어 db를 롤백하지 않는다.

  2. 테스트 종료 시 @AfterEach로 데이터 삭제

    모든 데이터를 지워야 한다는 단점, 연관관계가 있는 경우 비효율적

  3. 매 테스트 이후 Truncate 쿼리로 모든 테이블 초기화

    • @Sql 어노테이션 사용

      Test 상단에 @Sql 어노테이션을 통해 ddl 전달

      테이블 추가될 때마다 파일을 수정해야한다는 단점

    • EntityManager로 Truncate 쿼리 실행

      JPA에서 쿼리를 직접 만들 수 있는 EntityManager를 빈으로 주입받고 모든 테이블 이름을 받아 인수테스트 시 TRUNCATE 쿼리 실행

      • 참고: h2 truncate는 restart with 1이 필요
      @Component
      @Profile("test")
      public class DatabaseCleaner implements InitializingBean {
          @PersistenceContext
          private EntityManager entityManager;
           
          private List<String> tableNames;
           
          @Override
          public void afterPropertiesSet() {
              tableNames = entityManager.getMetamodel().getEntities().stream()
                      .map(entry -> entry.getName().toLowerCase(Locale.ROOT))
                      .collect(Collectors.toList());
          }
           
          @Transactional
          public void execute() {
              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();
          }
      }
      
  4. @DirtiesContext

    @DirtiesContext 어노테이션을 이용해 생성된 컨텍스트를 더럽히고 항상 새로운 컨텍스트를 생성하도록 하는 것이다.

    시간이 오래 걸린다.