JPA

[JPA] auto-increment 테스트 실패 원인 및 해결

코리늬 2020. 3. 3. 20:48

기존에 각각 메소드 단위로 단위 테스트를 마치고 PR을 하여 Merge 되었던 소스가 있었다.

근데 이거 전체로 돌리니까 ID 값이 삭제가 안되었는데 ??

  • 응???

 

다음날 바로 전체 테스트 케이스를 돌려보았더니 테스트가 깨졌다.

뭐지 merge..?

 

원인 분석

@After 어노테이션을 사용해 각 메소드를 테스트 할 때 마다 usersRepository.deleteAll() 하도록 설정을 해놓은 상태였다.

delete 쿼리는 매 테스트 마다 잘 수행이 되었지만, 우리의 auto-increment Id님 께서는 1,2,3 순차적으로 증가를 하고계셨다.

ㅎㅎ

이동욱님 블로그에서도 비슷한 사례로 Entity의 Id 칼럼의 GeneratedValue 전략을 GenerationType.IDENTITY 로 지정해 해결 한 사례가 있지만, 나는 이미 이렇게 설정 한 상태였다.

구글링을 통해 알게되었는데

@After에서 usersRepository.deleteAll()를 통해 이전 테스트에서 생성된 레코드를 지우더라도, auto-increment에 의해 두 번째 실행되는 메소드에서 생성되는 레코드의 id는 2가 된다.

그래서 내가 단독으로 테스트 했을 때는 다 통과가 되었던것이다!

단 하나의 메소드만 실행하므로 auto-increment에 영향을 받지않는다!

 

해결 방법

매번 auto-increment값을 초기화 해주고 싶다면 H2 DB에서는 아래와 같이 설정해줘야한다.

@After
    public void teardown() {
        usersRepository.deleteAll();
        this.entityManagr
            .createNativeQuery("ALTER TABLE users ALTER COLUMN `id` RESTART                                                                     WITH 1")
            .executeUpdate();
    }

그런데 또 문제가 있다.

 

이 방법은 @DataJpaTest @Transactional이 적용되는 서비스 클래스를 테스트 할 때는 적용 가능하지만,

@SpringBootTest( webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT )를 사용하는 환경에서는 사용 할 수 없다.

 

@SpringBootTest 에서 tearDown 메소드를 실행하면

javax.persistence.TransactionRequiredException: Executing an update/delete query

에러가 발생한다.

 

Transaction을 적용하기 위해 @Transactional을 붙여주더라도, deleteAll()을 해도 제대로 사라지지 않는 현상을 볼 수 있다..!!

 

다른 방법

테스트 클래스에 아래 어노테이션을 사용한다.

@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)

auto-increment 문제는 해결되지만, 테스트 메소드 하나하나 실행 될 때 마다 applicationContext를 새로 만들기 때문에 테스트 실행 속도가 매우 느려지므로 권장되는 방법은 아니다.

 

그래서 특정 id의 조회가 아니고서야 id에 대한 테스트는 존재 유무만 확인 하기로 했다.

끝.