@DataJpaTest
를 적용하고 @BeforeAll
을 이용하여 테스트 전 공통적인 행동을 정의하려고 했는데, 테스트를 두 번 진행하게 되면 데이터베이스 정합성 문제가 발생하는 에러가 발생하였습니다.이유는 unique여야 하는 id값이 중복되었기 때문이었어요. 하지만 첫 테스트 시에는 데이터베이스 정합성 문제가 발생하지 않아 데이터베이스를 확인하였습니다.
@DataJpaTest
를 이용해 @Transactional
이 적용되어 데이터베이스 롤백이 진행되어야 했지만, 진행되지 않아 테스트를 다시 실행하게 되면 같은 유저가 다시 저장되는 문제가 발생하는 것이었습니다.
그렇다면, 왜 @Transactional
을 이용한 데이터베이스 롤백이 진행되지 않은 것일까요?
스프링에서 @Transactional
은 AOP로 구현됩니다.
@Transactional
을 사용하기 위해서는 스프링이 프록시 객체를 만들어서 구현합니다.
그리고 Junit의 @BeforAll
은 무조건 static 메소드로 구현되어야 합니다.
여기서 Static 메서드에 대한 프록시는 만들 수 없다는 사실이 문제의 원인이었습니다.
Java에서 static 키워드를 사용한다는 것은 메모리에 한번 할당되어 프로그램이 종료될 때 해제되는 것을 의미합니다.
static 변수와 static 메소드는 객체가 생성되기 이전에 이미 메모리 영역에 할당되어 있습니다. 그렇기 때문에 객체의 생성 없이 바로 접근할 수 있습니다.
프록시 객체를 만드려면 호출된 객체의 바이트코드를 조작해서 만드는데 static은 객체 생성 없이 바로 접근하는 것이므로 불가능했던 것입니다.
@Transactional
이 DB를 무조건적으로 이전으로 롤백해주지 않는다는 것을 깨달았습니다.
Test를 작성할 때 @Transactional의 범위를 잘 파악하고 사용해야 하며 @Transactional이 프록시 객체를 기반으로 작동한다는 원리를 알 수 있었습니다.
@Transactional
에 대한 토비님의 좋은 글이 있어서 가져오면서 마무리하도록 하겠습니다!