Spring

@JdbcTest 때는 왜 schema.sql이 읽히지 않을까?

자바생 2023. 6. 9. 10:05
728x90

글을 쓰게 된 이유

 

 

JdbcTemplate을 사용하는 Dao 계층을 테스트하기 위해서 @JdbcTest 어노테이션을 사용하여 테스트를 작성했습니다.

 

 

이때 test 관련 yml 파일에서 DB url을 url: jdbc:h2:mem:testdb;MODE=MySQL 을 설정해 주었습니다.

 

 

 

하지만 schema.sql을 읽는 중에 H2 데이터베이스에 올바르지 않은 스키마라며 계속해서 테스트가 진행되지 않았습니다.

 

 

 

결론적으로는 제가 설정한 yml 파일을 읽지 않고, 다른 url을 가지고 있었습니다.

 

 

 

 

그러면 @JdbcTest를 사용하면 왜 제 yml을 읽지 않는 것이고, 설정된 yml 파일을 읽게 하려면 어떻게 해야 하는지 학습해 보았습니다.

 

 

 

 

 

@JdbcTest

 

 

 

먼저 JdbcTest 어노테이션을 보니 많은 어노테이션들을 가지고 있습니다.

 

 

 

여기서 제일 중요한 @AutoConfigureTestDatabase를 보겠습니다.

 

 

Annotation that can be applied to a test class to configure a test database to use instead of the application-defined or auto-configured DataSource. In the case of multiple DataSource beans, only the @Primary DataSource is considered.

 

 

 

 

공식 문서 설명을 보면 여러 DataSource 대신 사용할 테스트 데이터베이스를 구성하기 위해 테스트 클래스에 적용할 수 있는 어노테이션이라고 합니다.

 

 

public @interface AutoConfigureTestDatabase {

	@PropertyMapping(skip = SkipPropertyMapping.ON_DEFAULT_VALUE)
	Replace replace() default Replace.ANY;

	EmbeddedDatabaseConnection connection() default EmbeddedDatabaseConnection.NONE;

	enum Replace {

		ANY,
		AUTO_CONFIGURED,
		NONE

	}

}

 

 

 

옵션들이 중요한데 하나씩 살펴보겠습니다.

 

 

 

 

replace는 대체할 Datasource bean을 결정합니다.

옵션은 ANY, AUTO_CONFIGURED, NONE으로 나눠지는데,

ANY는 DataSource bean을 무조건 바꾸고, AUTO_CONFIGURED는 자동 구성된 경우에만 DataSource 빈을 교체하고, NONE은 교체하지 않는 것입니다.

 

 

 

 

그러고 나서 connection에서 임베디드 데이터베이스 연결을 시도합니다.

당연히 replace 옵션에 따라 동작될지 안될지 결정될 것입니다.

 

 

 

replace의 default는 ANY 이기 때문에 무조건 DataSource bean을 교체할 것이고, connection 메서드가 실행됩니다.

 

 

 

EmbeddedDatabaseConnection을 들어가 보면 Enum 클래스로 다양한 임베디드 DB에 관한 설정들이 들어 있습니다.

 

 

 

 

우리는 설정을 H2를 사용했기 때문에 해당 클래스에서 자동적으로 아래 enum을 적용시켰습니다.

 

 

H2(EmbeddedDatabaseType.H2, DatabaseDriver.H2.getDriverClassName(),
			"jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE", (url) -> url.contains(":h2:mem")),

 

 

 

그래서 기존에 있던 yml을 무시하고, DataSource에 관한 빈 설정을 해당 설정으로 변경시키기 때문에 MODE=MySQL 이 제대로 동작하지 않게 되어 스키마에서 익셉션이 발생하게 됩니다.

 

 

그렇다면 테스트용 스키마를 따로 만들어야 할까?

 

 

 

결론부터 말씀드리면 따로 만들지 않아도 됩니다.

 

 

기존에 있던 yml 파일을 읽어 DataSource 빈 설정을 할 수 있습니다.

 

 

 

아까 위에서 말한 replace 옵션을 변경해 주면 됩니다. ANY라는 옵션으로 인해 항상 DataSource 옵션이 변경되었으니까요

 

 

 

@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)

 

를 통해서 Replace 옵션을 NONE으로 변경해 준다면 임베디드 DB DataSource 빈을 재설정하지 않게 됩니다.

 

 

 

결과

 

 

@JdbcTest
class LineDaoTest {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    private LineDao lineDao;

    @BeforeEach
    void setUp() {
        lineDao = new LineDao(jdbcTemplate);
    }

    @Test
    @DisplayName("findByLineName() : 라인 이름으로 라인을 찾을 수 있다.")
    void test_findByLineName() throws Exception {
        //given
        final String lineName = "1호선";

        //when
        final Optional<LineEntity> savedLine = lineDao.findByLineName(lineName);

        //then
        assertAll(
                () -> assertThat(savedLine).isPresent(),
                () -> assertEquals(1L, savedLine.get().getId()),
                () -> assertEquals(savedLine.get().getName(), lineName)
        );
    }

 

 

기존 저의 테스트 코드입니다.

 

 

컨테이너가 뜰 때, 아래와 같은 로그가 작성됩니다.

 

 

 

 

위에서 작성됐던 코드와 똑같은 것을 알 수 있습니다.

 

H2(EmbeddedDatabaseType.H2, DatabaseDriver.H2.getDriverClassName(),
			"jdbc:h2:mem:%s;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE", (url) -> url.contains(":h2:mem")),

 

 

이때는 제가 작성한 yml 파일이 아닌 임베디드 DB 설정을 읽어서 DataSource 빈을 수정했습니다.

 

 

 

 

 

그래서 MySQL에서는 지원하지만 H2에서는 지원하지 않은 쿼리에 대해서 exception 이 발생하게 됩니다.

 

 

H2에서 MODE=MySQL 이 없어졌기 때문이죠.

 

 

@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)

 

 

해당 어노테이션을 추가해 보겠습니다.

 

 

 

아까와 다르게 DB URL 로그가 찍히지 않았고, 테스트가 성공적으로 동작했습니다.

 

 

 

 

 

 

 

 

 

 

 

 

728x90