바로 원인을 분석하기 위해 에러 메시지를 확인해본결과, 쿼리를 실행할 때 문제가 발생한다는 것을 알 수 있었고 디버깅해본 결과, Member Entity를 저장하는 로직에서 UndeclaredThrowableException이 발생하는 것을 확인할 수 있었다.
UndeclaredThrowableException 은 여러 상황에서 발생할 수 있지만, 데이터를 저장할 때 이러한 예외가 발생하는 경우는 주로 Unique 제약 조건을 걸어둔 필드에 중복된 값을 삽입하려할 때이다.
E2I 팀은 Test Fixture 을 미리 정의하여 사용하고 있었기 때문에 이전 테스트에서 삽입한 값이 Rollback 되지 않는다면 반드시 예외가 발생한다. 현재까지의 상황을 미루어 보았을 때, 테스트 격리가 제대로 이루어지지 않아 발생한 문제라는 생각이 들었다.
테스트 클래스에 @Transactional어노테이션을 붙어놓았기 때문에, 당연히 이전 테스트에서 삽입한 데이터는 Rollback 될 것이라 생각했다. @SpringBootTest 에서 @Transactional의 Rollback 기능이 수행되지 않는 조건이 존재하는지 확인하고자 이와 관련된 자료를 찾아보게 되었고, 다음과 같은 정보를 찾아낼 수 있었다.
관련된 내용을 찾아본 결과, RANDOM_PORT 혹은 DEFINED_PORT를 사용하는 경우 HTTP client와 Spring 서버 자체가 별도의 쓰레드에서 실행되므로 테스트와 트랜잭션이 하나로 묶일 수 없어, rollback 기능이 수행되지 않는 것이다.
RestAssured는 실제 스프링 컨테이너를 띄워 실행하는 테스트 프레임워크이기 때문에 RANDOM_PORT / DEFINED_PORT 설정이 불가피하다. 따라서, 별도의 쓰레드에서 스프링 컨테이너가 실행되고, 이로 인해 @Transactional 의 Rollback이 수행되지 않아, 테스트간 격리가 이루어지지 않던 것이었다.
- deleteAllInBatch() -> 테이블의 전체 데이터를 지우는 벌크성 쿼리이다. 어떤 데이터를 먼저 지울지 고민해야함 deleteAllInBatch()에서는 delete 쿼리만 발생
- deleteAll() -> 마찬가지로 테이블의 전체 데이터를 삭제한다. -> DELETE 문 이외에도, 여러 건의 파생 쿼리 발생 -> 이는 연관된 엔티티가 있는지 확인하고 해당 엔티티를 지워주고자하는 동작으로 인해 발생하는 쿼리 -> 실제 내부 로직을 확인해보면 findAll() 작업이 한 번 더 수행됨 deleteAll()에서는 여러건의 쿼리 발생
바로 원인을 분석하기 위해 에러 메시지를 확인해본결과, 쿼리를 실행할 때 문제가 발생한다는 것을 알 수 있었고 디버깅해본 결과, Member Entity를 저장하는 로직에서 UndeclaredThrowableException이 발생하는 것을 확인할 수 있었다.
UndeclaredThrowableException 은 여러 상황에서 발생할 수 있지만, 데이터를 저장할 때 이러한 예외가 발생하는 경우는 주로 Unique 제약 조건을 걸어둔 필드에 중복된 값을 삽입하려할 때이다.
E2I 팀은 Test Fixture 을 미리 정의하여 사용하고 있었기 때문에 이전 테스트에서 삽입한 값이 Rollback 되지 않는다면 반드시 예외가 발생한다. 현재까지의 상황을 미루어 보았을 때, 테스트 격리가 제대로 이루어지지 않아 발생한 문제라는 생각이 들었다.
테스트 클래스에 @Transactional어노테이션을 붙어놓았기 때문에, 당연히 이전 테스트에서 삽입한 데이터는 Rollback 될 것이라 생각했다. @SpringBootTest 에서 @Transactional의 Rollback 기능이 수행되지 않는 조건이 존재하는지 확인하고자 이와 관련된 자료를 찾아보게 되었고, 다음과 같은 정보를 찾아낼 수 있었다.
관련된 내용을 찾아본 결과, RANDOM_PORT 혹은 DEFINED_PORT를 사용하는 경우 HTTP client와 Spring 서버 자체가 별도의 쓰레드에서 실행되므로 테스트와 트랜잭션이 하나로 묶일 수 없어, rollback 기능이 수행되지 않는 것이다.
RestAssured는 실제 스프링 컨테이너를 띄워 실행하는 테스트 프레임워크이기 때문에 RANDOM_PORT / DEFINED_PORT 설정이 불가피하다. 따라서, 별도의 쓰레드에서 스프링 컨테이너가 실행되고, 이로 인해 @Transactional 의 Rollback이 수행되지 않아, 테스트간 격리가 이루어지지 않던 것이었다.
- deleteAllInBatch() -> 테이블의 전체 데이터를 지우는 벌크성 쿼리이다. 어떤 데이터를 먼저 지울지 고민해야함 deleteAllInBatch()에서는 delete 쿼리만 발생
- deleteAll() -> 마찬가지로 테이블의 전체 데이터를 삭제한다. -> DELETE 문 이외에도, 여러 건의 파생 쿼리 발생 -> 이는 연관된 엔티티가 있는지 확인하고 해당 엔티티를 지워주고자하는 동작으로 인해 발생하는 쿼리 -> 실제 내부 로직을 확인해보면 findAll() 작업이 한 번 더 수행됨 deleteAll()에서는 여러건의 쿼리 발생