만족은 하되 안주하지는 말자

기록해야 기억한다

프로그래밍/JAVA

[Spring] @Sql 어노테이션을 test class에서 1번만 실행시키려면?

D36choi 2023. 1. 30. 00:12
728x90

문제

@Sql(value = "/db/cossc/test.sql")
@SpringBootTest
@ActiveProfiles("test")
class QuizServiceTest {

    @Autowired
    private QuizRepository quizRepository;


	@Test
    public void test() {
    // ...테스트 코드
    }
    
    

	@Test
    public void test2() {
    // ...테스트 코드
    }
    
  

	@Test
    public void test3() {
    // ...테스트 코드
    }
}
// 나의 경우엔 quiz test를 위해선 의존관계가 있는 tag entity를 초기화해줘야했다

// test.sql

INSERT INTO cossc.TAG (tag_id, created_date, updated_date, name) VALUES (1, '2023-01-29 23:49:51', '2023-01-29 23:49:52', 'python');
INSERT INTO cossc.TAG (tag_id, created_date, updated_date, name) VALUES (2, '2023-01-29 23:49:59', '2023-01-29 23:50:02', 'java');
INSERT INTO cossc.TAG (tag_id, created_date, updated_date, name) VALUES (3, '2023-01-29 23:50:01', '2023-01-29 23:50:03', 'javascript');

spring test 를 작성하다보면, persistence test를 할때, 다른 테이블의 데이터들을 초기화해줘야할 때가 있다.

이럴땐 jpa code로 생성시켜주거나 sql로 insert해주는 방법이 있는데, 

jpa code로 하면 반복적이고 코드가 불필요하게 길어지는 것 같아 sql insert를 이용하려 했다.

 

 

[Mysql] Duplicate entry ' ' for key 'PRIMARY' 

 

 

하지만, 테스트를 실행시켜보면, duplicate key sql error 가 발생한다.

왜냐면, @Sql 어노테이션에 할당된 쿼리가 매 테스트코드 실행시에 동작하기 때문이다.

@Sql 어노테이션은 스프링의 아래 리스너에 의해 sql 을 찾아서 실행하도록 돕는다.

SqlScriptsTestExecutionListener

그리고 위 리스너는 beforeTestMethod, AfterTestMethod 를 가진다.

-> 즉, 메서드 별로 실행되는게 기본이라는 뜻이다.

 

그럼 이걸 어떻게 해야, 클래스 실행 단위로 sql 실행을 할 수 있을까?

즉, junit 의 @beforeAll 메서드가 실행되는 시점에만 sql를 실행하고 싶은 것이다.

해결방법

    @BeforeAll
    public static void setup(@Autowired DataSource dataSource) {
        try (Connection conn = dataSource.getConnection()) {

            ScriptUtils.executeSqlScript(conn, new ClassPathResource("/db/cossc/test.sql"));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

위처럼, sql 어노테이션이 아닌 BeforeAll 어노테이션을 단 메서드에 별도 실행 시켜주면 된다.

ScriptUtils는 아래 패키지에 위치해 있으며, @Sql 에서 사용하는 ExecutionListener에서도 사용하고 있으므로, 직접 사용해도 큰 차이는 없을 것이라고 생각한다.

springframework.jdbc.datasource.init

 

 

출처

https://stackoverflow.com/questions/47775560/how-can-springs-test-annotation-sql-behave-like-beforeclass

답변 저자(https://stackoverflow.com/users/4681265/adrian)

 

How can Spring's test annotation @Sql behave like @BeforeClass?

How can I tell the @Sql annotation to run only once for the class, and not for each @Test method? Like having the same behaviour as @BeforeClass? @org.springframework.test.context.jdbc.Sql(

stackoverflow.com