20230722_테스트코드에서 @Transactional
토비의 스프링 인프런 강의에서 질문으로 올라온 사항인데 토비님이 직접 유튜브 라이브에서 해당 내용에 대한 답변을 남겨주셨다 https://www.inflearn.com/questions/792383/테스트에서의-transactional-사용에-대해-질문이-있습니다 추가로 해당 건에 대해서 회사에서도 테스트 코드에서 @Transactional 을 붙히는 케이스를 본 적 있기 때문에 궁금증에 해당 내용을 정리해보고자 한다
실제 메인 소스에서의 @Transactional 애노테이션의 의미는 해당 애노테이션이 붙어있는 부분을 메소드의 단위를 하나의 트랜잭션으로 묶겠다는 의미이다 하지만 반대로 테스트코드에서는 @Transactional 은 어떤 무엇일까? 우선 해당 애노테이션의 의미는 이러하다 테스트 코드에서 인 메모리든 리얼 디비든 어떠한 데이터베이스를 사용해서 테스트가 진행되는 테스트 코드인 경우, 해당 애노테이션을 통해서 미리 트랜잭션을 시작해두는 것이다 그렇게 되면 실제 메인 소스에서의 코드에서의 트랜잭션이 @Transactional 의 default 전이 레벨인 REQUIRED 으로 되어있기 떄문에 소스 기준으로 봤을 때 상위인 테스트 코드에서 선언해둔 @Transactional 에 붙게 된다는 것이다 그렇게 붙고 나서는 해당 애노테이션이 붙은 테스트가 종료되는 시점에 실제로 디비에 커밋할 것인가 / 롤백한 것인가 에 대한 것을 정해줄 수 있다는 것이다
그래서 이걸 테스트 코드에서 사용하는 이유가 뭔가? > 테스트코드의 롤백을 위해서 이다 테스트를 하는데 있어서 롤백을 하는 이유는 단순하다 많은 테스트 코드를 진행하면서 그 모든 테스트 코드가 하나의 디비(인메모리든 실제 디비든)를 바라보고 테스트가 돌아갈텐데 테스트를 돌리게 되었을 때 테스트 데이터들이 들어가 있으면 내가 원하는 대로 테스트가 진행되지 않을 것이기 떄문이다 그래서 테스트가 끝나면 해당 테스트 코드가 돌면서 진행되었던 데이터들을 다시 원래대로 되돌리는, 즉 롤백하는 방법들이 있고 뭐 각각의 데이터를 테스트가 종료되는 시점에 직접 하나하나 되돌리는 방법도 있고, 해당 테스트 코드에 @Transactional 을 통해서 전체 롤백을 진행하는 방법도 있다
테스트 코드에서 @Transactional 을 사용하는 방식 자체는 뭔가 구글링을 막 해보면 사용하지 말라 라는 결론으로 항상 나아가곤 한다 그래서 왜 사용하지 말라는걸까?? 바야흐로 스프링 버전 1 시절, 이때는 스프링에서 사용하라고 권장하고 그랬다고 한다 질문을 올려주신 분을 기준으로해서 봤을 때 실제 메인 단에서 @Transactional 을 통해서 트랜잭션 범위를 잘 만들어두었는데 테스트 코드에 달아둔 @Transactional 때문에 코드 전체로 묶이게 되면서 원하는대로 동작하지 않는 이슈로 보인다 > 이렇게 하게되면 확실하게 내가 원하는 대로 코드를 테스트할 수 없다고 생각한다
여기서 해주신 질문은 이러하다 > 테스트 코드에서 @Transactional 말고 내가 원하는대로 디비의 모든 롤백를 일어나게 하는 그러한 애노테이션은 있는가? 아니면 각 테스트가 끝날 때마다 cleanup 하는 방법은 또 뭐가 있는가?
이에 대한 토비님의 답변은 이러하다 토비님 같은 경우에는 옛날에는 dbunit 이라는 도구를 사용해서 테스트 전후로 데이터를 관리하는 그러한 도구를 사용해서 진행하다가 @Transactional 이 나오고 난 이후에는 해당 애노테이션을 사용해서 테스트코드의 관리를 했다고 한다 근데 문제는 하나의 테스트 내에서 2개의 트랜잭션이 만들어져야 하는 경우에 해당 방법을 사용하게 되면 결국은 단 하나의 트랜잭션만 생성되기 떄문에 원하는대로 테스트할 수 없는 경우가 생긴다 옛날에 orm 을 사용할 때에는 직접 트랜잭션을 실행시키고 했어야 하는데 jpa 같은 경우에는 알아서 트랜잭션을 열어준다 jpa 에서 데이터를 밀어넣는 테스트를 할 때 데이터를 등록만 하고 그것을 조회하는 방법을 하지 않는 상황에서는 무조건적으로 flush 작업을 해주어야 한다
결국 결론은 이러하다 > 쓰는 것을 추천한다 이러한 위험성을 가지고 있지만 간편하게 해당 애노테이션을 통해서 테스트 코드의 롤백을 사용할 수 있기 때문, 모든 테이블에 대한 aftereach 를 구현하는 방법 자체는 그렇게 좋은 방법도 아닐뿐더러 그리고 여러 트랜잭션이 발생해야 하는 테스트 케이스에서도 강제로라도 트랜잭션을 관리해서 테스트를 진행하면 된다 대신 문제가 생길 수도 있다는 점을 인지하고 있는 상황에서 진행하면 좋다 사실 모든 테스트 코드가 100% 완벽할 수는 없기 때문에 다양한 테스트들을 통해서 100%를 위해 노력해야한다는 점이 중요하다 이 반대로 db 를 비우는 방향으로 테스트 코드를 만들게되면 굉장히 실수하는 케이스가 발생할 수 밖에 없다고 생각한다고 하신다
정말 raw 하게 노트 필기를 진행해봤는데 뭔가 내가 정리를 잘 못해서 그냥 정보를 쏟아붙는 방식으로만 정리한 것 같다 결론은 결국 이러하다 > 테스트 코드에서의 @Transactional 은 사용하기 편리하고 좋은 기능을 제공해준다 그래서 사용하면 좋긴 하지만 다양한 트랜잭션 케이스에서는 원하는대로 테스트 코드가 돌지 않는 경우가 생길 수 있다 afterEach 로 구현하지 않는 다면 해당 방식으로 사용하는 것도 좋은 방법이라고 보이긴 하지만 현재 내가 테스트 코드를 짜온 방식으로는 뭔가 그러한 위험성에 대해서 리스트를 안고가는 것에 대한 의구심이 들긴 한다 무엇보다 우리 팀 같은 경우에는 테스트 코드의 작성을 위해서 테스트 코드를 위한 MSA mocking 방식이나 afterEach 와 테스트를 위한 세팅을 정해두고 진행하기 떄문에 난 역시 아직도 afterEach 에 대한 사용방식이 좋다는 생각이 들긴 한다 앞으로 기존 방식이 아닌 테스트 코드를 구현하는 방식을 고민할 떄 한번 사용해보자
Last updated
Was this helpful?