spring boot/테스트

spring 테스트 마다 table 초기화 시키는 방법

junjunjun 2023. 1. 17. 20:40
반응형

 

  각 테스트 케이스를 개별적으로 실행할 경우 잘 통과되는데, 클래스로 전체를 한 번에 실행할 경우 실패하는 경우가 있다.

나의 경우 엔티티의 id값이 테스트를 진행하면서 증가되는 문제점이 있었다. (물론 개별적으로 진행하면 성공함)

 

이러한 문제점을 해결하기 위해 매 테스트마다 테이블 데이터를 초기화시켜 주는 작업을 추가시켜 주었다.

    // 원하는 결과물
    @BeforeEach
    void beforeEach() {
        databaseCleaner.execute();
    }

 

DatabaseCleaner 클래스 생성

전체적인 흐름

  • 최초로 한 번 db에 들어있는 table 이름을 받아서 list에 담아줍니다. 
  • 매번 테스트가 진행되기 전 table의 데이터를 초기화해줍니다.

전체 코드

@Component
public class DatabaseCleaner {
    @PersistenceContext   // entityManager 전용 @Autowired
    private EntityManager entityManager;

    private List<String> tableNames;  // table 이름을 담을 리스트

    @PostConstruct // spring 생명주기에서 의존관계 주입까지 마친 상태에서 실행되는 어노테이션
    public void init() {  // (1)
        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 execute() {  // (2)
        entityManager.flush();
        entityManager.createNativeQuery("SET foreign_key_checks = 0").executeUpdate();

        for (String tableName : tableNames) {
            entityManager.createNativeQuery("TRUNCATE TABLE " + tableName).executeUpdate();
        }

        entityManager.createNativeQuery("SET foreign_key_checks = 1").executeUpdate();
    }
}

(1) init 작업

@Entity 붙은 클래스 이름을 Camel case에서 Snake case로 바꿔준 뒤 리스트에 저장한다.

ex) PostLike -> post_like

작업 수행 후 모든 테이블 명이 list에 담긴다.

 

db에 들어가는 table명을 알아야지 명령어를 통해 테이블 초기화 작업을 수행할 수 있다.

 

+ CaseFormat을 쓰기 위해 의존성을 추가해줘야 한다.

// google guava
implementation group: 'com.google.guava', name: 'guava', version: '31.1-jre'

 

(2) execute 작업

  1. 외래키 제약 조건을 꺼준다.
  2. table 데이터를 지워준다.
  3. 외래키 제약 조건을 켜준다.

데이터베이스 종류마다 명령어가 조금씩 다를 수 있습니다. 위 코드의 개발환경은 mysql입니다.

- TRUNCATE를 쓰는 이유
delete의 경우 row 하나씩 삭제해 주기 때문에 느리지만 truncate는 한 번에 삭제해 줘서 속도가 빠르다.

 

혹시 이런 에러가 뜬다면 

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'databaseCleaner' defined in file End\build\classes\java\test\com\masil\common\DatabaseCleaner.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException

DB의 테이블명이 제대로 tableList에 담겼는지 확인해 보자.

 

참고자료

반응형