@Sql로 테스트 데이터 사용 시 유의할 점
Spring 테스트를 작성하면서 @Sql 애노테이션을 통해 주입한 테스트용 데이터가 실제로 들어오지 않는 문제가 발생했다.
사용자 로그인 검증이 포함되는 엔드포인트를 테스트해야 해서 아래와 같이 로그인을 자동화하기 위해 @BeforeAll을 사용했는데, 테스트를 돌려보니 로그인이 실패하고 있었다.
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@TestInstance(TestInstance.Lifecycle.PER_CLASS) // 혹시 몰라서 TestInstance 하나만 두도록 하고 BeforeAll에서 static을 뺌 (참고: BeforeAll은 원래 static 메서드로 구현해야 함)
@Sql("/member-data.sql")
class AdminPageControllerTest {
private String cookie;
@BeforeAll
void loginWithAdmin() {
LoginMember admin = LoginMemberFixture.getAdmin();
cookie = RestAssured
.given().log().all()
.body(new LoginRequest(admin.getPassword(), admin.getEmail()))
.contentType(MediaType.APPLICATION_JSON_VALUE)
.when().post("/login") // 여기서 로그인이 실패함
.then().log().all().extract().header("Set-Cookie").split(";")[0]; // 그래서 파싱할 쿠키가 없음
}
}
@Sql로 전달한 파일에는 필요한 회원 정보를 테이블에 저장하는 쿼리가 구현되어 있다. 그럼에도 불구하고 로그인이 실패하는 것을 보니, SQL 파일이 로딩되기 전에 @BeforeAll 메서드가 실행된다고 의심해 볼 수밖에 없었다. 추가로, @BeforeEach로 테스트를 돌리면 문제가 전혀 발생하지 않았기 때문에 @BeforeAll만 다르게 동작하는 것 같아 보였다.
회원 정보 테이블이 비어있을 것을 가정하고 로그인 서비스 내 메서드를 디버깅해보니, 예상대로 repository가 빈 Optional 객체를 반환하고 있었다.
찾아보니 @BeforeAll이 @Sql 전에 호출되는 것은 의도된 동작이다. @BeforeEach는 @Sql 이후에 동작하지만, @BeforeAll은 반대인 것이다.
만약 @Sql이 @BeforeAll 전에 실행되게 하려면 executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS를 속성으로 추가해 문제를 해결할 수 있다.
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT)
@Sql(value = "/member-data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_CLASS)
class AdminPageControllerTest {
private static String cookie;
@BeforeAll
static void loginWithAdmin() {
LoginMember admin = LoginMemberFixture.getAdmin();
// 정상 동작함
cookie = RestAssured
.given().log().all()
.body(new LoginRequest(admin.getPassword(), admin.getEmail()))
.contentType(MediaType.APPLICATION_JSON_VALUE)
.when().post("/login")
.then().log().all().extract().header("Set-Cookie").split(";")[0];
}
}
다만 이 방법도 한계가 있다. 만약 @DirtiesContext를 테스트 메서드 별로 적용한다면 @Sql로 넣은 데이터가 초기화돼서 테스트에 지장을 준다.
@BeforeAll + @Sql 조합을 사용하겠다고 한다면 데이터를 변경하지 않는 단순 접근 테스트(예를 들어 어드민 전용 뷰 접근에 대한 테스트)에 유용할 것 같다.
참고: