티스토리 뷰

기타

[Error] @DataJpaTest DataSource 설정 오류

CharlieZip 2021. 12. 19. 14:50
반응형

Error 상황

프로젝트 환경 설정

application.yml

spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/awss3?serverTimezone=UTC&characterEncoding=UTF-8
    username: root
    password: 1234

  jpa:
    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
    open-in-view: false
    show-sql: true
    hibernate:
      format_sql: true
      ddl-auto: create

 

 

JPA관련 테스트를 하기 위해 @DataJpaTest를 이용해서 테스트를 실행하고자 하였다.

테스트를 작성하고 실행을 하니 아래와 같은 에러가 발생하였다.

에러내용중에서 화살표 부분의 에러내용을 가져와봤다.

Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: 
Error creating bean with name 'dataSourceScriptDatabaseInitializer' defined in class path resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]: 
Unsatisfied dependency expressed through method 'dataSourceScriptDatabaseInitializer' parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'dataSource': Invocation of init method failed; nested exception is java.lang.IllegalStateException: 
Failed to replace DataSource with an embedded database for tests. 
If you want an embedded database please put a supported one on the classpath or tune the replace attribute of @AutoConfigureTestDatabase.

에러내용을 보니 dataSourceScriptDatabaseInitializer인 dataSource 빈을 생성하는데 에러가 발생하고, Failed to replace DataSource with an embedded database for tests. DataSource를 내장 데이터베이스로 교체하는데에 실패했다고 한다.

If you want an embedded database please put a supported one on the classpath or tune the replace attribute of @AutoConfigureTestDatabase. 마지막으로 @AutoConfigureTestDatabase 를 살펴보라고 알려주는거 같았다.

 

그래서 에러를 해결하기 위해 @AutoConfigureTestDatabase 에 대해 먼저 알아보자.

 

 

@AutoConfigureTestDatabase

@AutoConfigureTestDatabase 에 대해 알아보기 위해서는 먼저 @DataJpaTest 를 살펴봐야 합니다.

 

 

DataJpaTest

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@BootstrapWith(DataJpaTestContextBootstrapper.class)
@ExtendWith(SpringExtension.class)
@OverrideAutoConfiguration(enabled = false)
@TypeExcludeFilters(DataJpaTypeExcludeFilter.class)
@Transactional
@AutoConfigureCache
@AutoConfigureDataJpa
@AutoConfigureTestDatabase
@AutoConfigureTestEntityManager
@ImportAutoConfiguration
public @interface DataJpaTest { 
}

@DataJpaTest 를 사용하면 자동으로 여러 옵션들이 붙는다.

 

여기서 @Transactional 애노테이션도 있는걸 확인 할 수 있다. 즉 @DataJpaTest 를 사용하면 추가로 트랜잭션을 선언해줄 필요가 없고, 테스트가 끝나면 자동으로 롤백이 적용됩니다.

 

DataJpaTest 코드 위에 주석을 읽어보거나 여기를 살펴보면 They also use an embedded in-memory database (replacing any explicit or usually auto-configured DataSource). The @AutoConfigureTestDatabase annotation can be used to override these settings. 라고 하여

번역해보자면 내장된 메모리 데이터베이스를 사용하고, @AutoConfigureTestDatabase 를 사용하면 설정을 재정의 할 수 있다고 적혀있다.

 

이걸 읽어보니 왜 에러가 발생했는지 알 수 있었다.

 

@DataJpaTest 는 기본적으로 내장된 메모리 데이터베이스를 이용해 테스트를 실행해주는데 나는 물리적인 MySQL 데이터베이스를 연결해서 테스트를 할려고 하여 발생한 문제였다.

 

내장된 메모리 데이터베이스로 바꾸어 문제를 해결할 수도 있겠지만 MySQL을 사용해서 테스트를 하고 싶었다.

그러면 MySQL 데이터베이스를 연결해서 테스트를 할려면 어떤 설정을 재정의 해야하는지 @AutoConfigureTestDatabase 를 살펴보자.

 

 

AutoConfigureTestDatabase

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@ImportAutoConfiguration
@PropertyMapping("spring.test.database")
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()
    • embeddedDatabase에 관한 설정
    • ANY → 자동 or 수동 상관없이 DataSource 빈을 교체
    • AUTO_CONFIGURED → 자동 구성된 경우에만 DataSource 빈 교체
    • NONE → DataSource를 교체 하지 않음
  • connection()
    • embeddedDatabase 를 연결해준다.

 

EmbeddedDatabaseConnection

public enum EmbeddedDatabaseConnection {

    NONE(null, null, null, (url) -> false),

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

    DERBY(EmbeddedDatabaseType.DERBY, DatabaseDriver.DERBY.getDriverClassName(), "jdbc:derby:memory:%s;create=true",
            (url) -> true),

    HSQLDB(EmbeddedDatabaseType.HSQL, DatabaseDriver.HSQLDB.getDriverClassName(), "org.hsqldb.jdbcDriver",
            "jdbc:hsqldb:mem:%s", (url) -> url.contains(":hsqldb:mem:"));
  • EmbeddedDatabaseConnection 을 살펴보면 제공하는 DatabaseType이 H2, DERBY, HSQLDB 이 있는걸 확인할 수 있다.

 

 

TestDatabaseAutoConfiguration

@Configuration(proxyBeanMethods = false)
@AutoConfigureBefore(DataSourceAutoConfiguration.class)
public class TestDatabaseAutoConfiguration {

    @Bean
    @ConditionalOnProperty(prefix = "spring.test.database", name = "replace", havingValue = "AUTO_CONFIGURED")
    @ConditionalOnMissingBean
    public DataSource dataSource(Environment environment) {
        return new EmbeddedDataSourceFactory(environment).getEmbeddedDatabase();
    }

    @Bean
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    @ConditionalOnProperty(prefix = "spring.test.database", name = "replace", havingValue = "ANY",
            matchIfMissing = true)
    static EmbeddedDataSourceBeanFactoryPostProcessor embeddedDataSourceBeanFactoryPostProcessor() {
        return new EmbeddedDataSourceBeanFactoryPostProcessor();
    }
  • TestDatabaseAutoConfiguration를 보면 @ConditionalOnProperty 으로 AutoConfigureTestDatabase의 replace() 값에따라 bean을 등록해줍니다.
  • 기본값은 Replace.ANY 이므로 EmbeddedDataSourceBeanFactoryPostProcessor bean이 등록됩니다.

 

EmbeddedDatabase getEmbeddedDatabase() {
    EmbeddedDatabaseConnection connection = this.environment.getProperty("spring.test.database.connection",
            EmbeddedDatabaseConnection.class, EmbeddedDatabaseConnection.NONE);
    if (EmbeddedDatabaseConnection.NONE.equals(connection)) {
        connection = EmbeddedDatabaseConnection.get(getClass().getClassLoader());
    }
    Assert.state(connection != EmbeddedDatabaseConnection.NONE,
            "Failed to replace DataSource with an embedded database for tests. If "
                    + "you want an embedded database please put a supported one "
                    + "on the classpath or tune the replace attribute of @AutoConfigureTestDatabase.");
    return new EmbeddedDatabaseBuilder().generateUniqueName(true).setType(connection.getType()).build();
}

기존 흐름

  1. AutoConfigureTestDatabase 에서 replace() 값을 설정. 기본값은 ANY
  2. TestDatabaseAutoConfiguration 에서 replace() 값이 ANY이므로 EmbeddedDataSourceBeanFactoryPostProcessor를 빈으로 등록
  3. getEmbeddedDatabase() 에서 데이터베이스 connection 값을 가져온다.
  4. 현재는 내장된 데이터베이스 H2, DERBY, HSQLDB 중 어느것도 설정이 없으므로 NONE을 반환

 

getEmbeddedDatabase() 을 통해 설정이 되는데 Debug를 통해 값을 살펴보면

connection이 NONE인것을 확인할 수 있습니다. 그리고 NONE인 경우 에러가 발생하게 됩니다.

 

 

에러 해결

그러면 MySQL을 사용하기 위해서는 어떻게 해야할까요??

 

방법은 간단합니다. AutoConfigureTestDatabase 에서 설정을 EmbeddedDatabase를 사용하지 않게 바꿔줍니다.

@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
class GalleryRepositoryTest {
}

Replace 값을 NONE으로 설정해줍니다. Replace.NONE 일 경우 EmbeddedDatabase를 찾아 설정하지 않고 기존 애플리케이션에서 사용한 DataSource(저의 경우 MySQL)가 등록되게 됩니다.

 

 

 

참고자료

https://kangwoojin.github.io/programing/auto-configure-test-database/

https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTest.html

반응형
댓글
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함