Skip to content

TestExecutionListenr 간의 충돌 문제에 대해 알아보아요

Moly edited this page Dec 30, 2024 · 1 revision

인기 태그 기능 추가

해당 PR에서 데이터를 저장한 시점에 따라 조회가 되는지 아닌지에 대한 로직, 즉 시간에 의존한 테스트가 있어요.

시간은 개발자가 제어할 수 없는 영역이기 때문에 보통 mocking을 하거나 더미데이터를 사용해서 진행해요.

저는 시간 자체를 mocking하는 것보다는 더미데이터를 활용해서, 기간이 지난 데이터를 미리 넣어두는 것이 간편하다고 판단해서 해당 방식을 선택했어요. 더미데이터는 @Sql 어노테이션을 활용하면 작성한 sql 파일을 실행하여 저장할 수 있어요.

@Sql의 동작 원리

그렇다면 @Sql은 어떻게 동작할까요?

image

내부적으로 SqlScriptsTestExecutionListener에 의해 실행되어요.

SqlScriptsTestExecutionListener?

이름에서 유추해보면 "테스트에서 sql 스크립트 파일을 실행하기 위한 객체다" 라는 느낌이 나죠? 맞아요. 지정한 시점(모든 테스트 메서드 실행 전에 한 번, 또는 실행 후)에 sql script를 실행하는 객체에요.

SqlScriptsTestExecutionListener은 내부적으로 TestExecutionListener를 상속 받아요. 우리는 TestExecutionListener를 통해 테스트라는 이벤트가 발생할 경우 전후 동작을 지정할 수 있어요.

image

해당 인터페이스의 메서드를 보면 이해가 조금 더 쉬울 거에요.

잠깐 !

그런데 TestExecutionListener...? 조금 익숙하지 않나요? 사실 우리 서비스에서는 TestExecutionListener를 이미 사용하고 있어요.

테스트 시에 사용하는 데이터베이스가 하나이기 때문에 테스트에서 격리를 위해 사용한 DatabaseCleaner가 AbstractTestExecutionListener를 상속받고 있어요.

DatabaseCleaner는 매 메서드 실행 직전 데이터베이스의 모든 table을 truncate 해요.

바로 여기서 문제가 발생했어요.

문제

@Sql을 붙였는데 더미데이터가 생기지 않아요 ㅠㅠ

앞에서 더미데이터를 활용해서 테스트를 진행하려고 했다는 것, 기억하죠?

TagServiceTest에서 작성한 테스트는 예상대로라면 더미데이터를 넣었으니, 조회하는 로직에 대해 테스트가 통과해야 했어요.

image

그런데 테스트는 실패했어요.

image

테스트 결과를 봤을 땐, "예외 메시지는 더미데이터에서 저장한 데이터가 없다"였어요.

아니, 분명히 메서드 레벨에 @Sql을 넣었는데 없다뇨?

테스트 리스너의 순서!

image

모든 Service Layer의 테스트 클래스는 위의 클래스를 상속받아 격리를 하고 있어요. 따라서 데이터가 아예 없다는 것은 Sql 파일이 작동한 건지는 알 수 없지만, 적어도 DatabaseCleaner는 작동을 했다는 것을 의미해요.

그러면, SqlScriptsTestExecutionListener가 실행되지 않았던 것일까요?

image

디버깅을 해보면 분명히 SqlScriptsTestExecutionListener에 도달하고 실행이 되는 것을 파악할 수 있어요.

그러면 여기서 알 수 있는 사실이 있어요.

  1. SqlScriptsTestExecutionListener가 실행되어 데이터가 추가되었다.
  2. DatabaseCleaner도 실행되어 전체 데이터가 지워졌다.

무엇인가 순서의 문제가 있는 것 같죠?

TestContextManager를 디버깅해보면 현재 테스트에 수행되는 TestContext의 정보를 알 수 있어요.

image

역시나!

문제는 SqlScriptsTestExecutionListener가 먼저 실행이 되고, 가장 순서가 뒤인 DatabaseCleaner가 실행되면서 발생했어요.

그렇다면 어떻게 해결할 수 있을까요?

Order 조정하기

image

SqlScriptsTestExecutionListener의 구현을 보면 Order이 5000인 것을 알 수 있어요.

우리는 커스텀한 DatabaseListener가 SqlScriptsTestExecutionListener보다 빨리 실행되어야 해요. 즉 우선순위를 더 낮춰야 해요.

따라서 getOrders를 재정의하고, 가장 빨리 실행되도록 우선순위를 낮춰 SqlScriptsTestExecutionListener보다 늦게 실행되도록 했어요.

이렇게 순서를 재정의하고 다시 TextContextManager를 디버깅하게 되면 흐름은 다음과 같아요.

image

⚡️ 코드zap

프로젝트

규칙 및 정책

공통

백엔드

프론트엔드

매뉴얼

백엔드

기술 문서

백엔드

프론트엔드


Clone this wiki locally