programing

Liquibase를 사용하여 Spring Boot 애플리케이션에서 장치 테스트를 위한 메모리 내 H2 초기화

lastmoon 2023. 6. 21. 22:53
반응형

Liquibase를 사용하여 Spring Boot 애플리케이션에서 장치 테스트를 위한 메모리 내 H2 초기화

저는 봄 JPA 테스트에서 in-mem 데이터베이스를 여러 번 사용했지만 문제가 없었습니다.이번에는 좀 더 복잡한 스키마를 초기화해야 하며, 스키마에는 사용자 지정 이름이 있어야 합니다(도메인 모델의 일부 엔티티는 특정 카탈로그 이름에 연결됨).따라서 이러한 이유로 테스트가 완전히 동기화되고 스키마를 초기화하고 유지 관리하는 방식과 일치하도록 하기 위해 SpringData JPA 저장소 유닛 테스트가 실행되기 전에 Liquibase를 사용하여 메모리 내 H2 데이터베이스를 초기화하려고 합니다.

(참고: 당사는 Spring Boot 2.1.3.REASE MySql을 기본 데이터베이스로 사용하며, H2는 테스트에만 사용됩니다.

시작 시 Liquibase 실행을 설정하기 위해 Spring Reference 가이드를 따르고 있습니다.메이븐 POM에 다음 항목이 있습니다.

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.liquibase</groupId>
        <artifactId>liquibase-core</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-test-autoconfigure</artifactId>
        <scope>test</scope>
    </dependency>

테스트 파일은 다음과 같습니다.

 @RunWith(SpringRunner.class)
 @ContextConfiguration(classes = PersistenceTestConfig.class)
 @DataJpaTest
 public class MyRepositoryTest {

     @Autowired
     private MyRepository myRepository;

     @Test
     public void someDataAccessTest() {
         // myRepository method invocation and asserts here...
         // ...
     }
 }

앱 컨텍스트 클래스:

  @EnableJpaRepositories({"com.mycompany.myproject"})
  @EntityScan({"com.mycompany.myproject"})
  public class PersistenceTestConfig {

       public static void main(String... args) {
           SpringApplication.run(PersistenceTestConfig.class, args);
       }
  }

참조 가이드에 따르면,

기본적으로 Liquibase는 컨텍스트에서 (@Primary) 데이터 소스를 자동으로 할당하고 마이그레이션에 사용합니다.다른 데이터 소스를 사용해야 하는 경우 데이터 소스를 생성하고 @Bean을 @LiquibaseDataSource로 표시할 수 있습니다.두 개의 데이터 소스를 생성하려면 다른 소스를 생성하고 @Primary로 표시해야 합니다.또는 spring.liquibase를 설정하여 Liquibase의 기본 데이터 소스를 사용할 수 있습니다.[url,user,password]를(를) 외부 속성에 입력합니다.spring.liquibase.url 또는 spring.liquibase.user를 설정하면 Liquibase에서 자체 데이터 소스를 사용할 수 있습니다.세 가지 속성 중 하나라도 설정되지 않은 경우 해당 spring.datasource 속성의 값이 사용됩니다.

분명히, 저는 제 테스트가 Liquibase가 데이터베이스 초기화에 사용하는 것과 동일한 데이터 소스 인스턴스를 사용하기를 원합니다.따라서 처음에는 spring.liquibase를 제공하지 않고 spring.data source 속성을 지정하려고 했습니다.[url, user, password] 속성 - Liquibase가 기본 Primary Spring 데이터 소스를 사용한다고 가정합니다.

spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS corp
spring.datasource.username=sa
spring.datasource.password=

spring.jpa.hibernate.ddl-auto=validate

# LIQUIBASE (LiquibaseProperties)
spring.liquibase.change-log=classpath:db.changelog.xml
#spring.liquibase.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS corp
#spring.liquibase.user=sa
#spring.liquibase.password=
spring.liquibase.default-schema=CORP
spring.liquibase.drop-first=true

테이블을 만들어야 하는 CORP 스키마를 Liquibase가 찾지 못했기 때문에 작동하지 않았습니다.

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'liquibase' defined in class path resource  [org/springframework/boot/autoconfigure/liquibase/LiquibaseAutoConfiguratio n$LiquibaseConfiguration.class]: Invocation of init method failed; nested  exception is liquibase.exception.DatabaseException:  liquibase.command.CommandExecutionException: liquibase.exception.DatabaseException: liquibase.exception.LockException: liquibase.exception.DatabaseException: Schema "CORP" not found; SQL statement:
 CREATE TABLE CORP.DATABASECHANGELOGLOCK (ID INT NOT NULL, LOCKED BOOLEAN NOT NULL, LOCKGRANTED TIMESTAMP, LOCKEDBY VARCHAR(255), CONSTRAINT PK_DATABASECHANGELOGLOCK PRIMARY KEY (ID)) [90079-197] [Failed SQL: CREATE TABLE CORP.DATABASECHANGELOGLOCK (ID INT NOT NULL, LOCKED BOOLEAN NOT NULL, LOCKGRANTED TIMESTAMP, LOCKEDBY VARCHAR(255), CONSTRAINT PK_DATABASECHANGELOGLOCK PRIMARY KEY (ID))]

그래서 명시적인 spring.datasource 속성 정의를 꺼내 다음 Liquibase 속성만 제공했습니다.

 spring.jpa.hibernate.ddl-auto=validate

 # LIQUIBASE (LiquibaseProperties)
 spring.liquibase.change-log=classpath:db.changelog.xml
 spring.liquibase.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS corp
 spring.liquibase.user=sa
 spring.liquibase.password=
 spring.liquibase.default-schema=CORP
 spring.liquibase.drop-first=true

그 결과 Liquibase 작업이 성공적으로 실행되고 시작 시 제공된 변경 로그 파일을 사용하여 필요한 모든 테이블과 데이터를 기본 데이터 소스에 로드하는 것처럼 보입니다.제가 Liquibase DS 속성을 명시적으로 설정했기 때문에 이러한 현상이 발생하는 것으로 알고 있으며, Spring 문서에 따라 Liquibase가 자체 기본 데이터 소스를 사용하게 됩니다.따라서 Liquibase 작업이 성공적으로 실행되는 동안 테스트가 다른 [Spring default?] 데이터 원본을 사용하려고 시도하고 데이터베이스 스키마가 테스트 전 유효성 검사에 실패합니다.("corp" 스키마를 찾을 수 없고 테이블도 찾을 수 없습니다.)따라서 테스트에서 Liquibase를 사용하여 생성하려는 인스턴스와 다른 데이터 소스 인스턴스를 사용하는 것이 분명합니다.

Liquibase가 생성하는 것을 사용하여 테스트를 수행하려면 어떻게 해야 합니까?

제가 시도하는 것은 아무것도 효과가 없는 것 같습니다.제가 사용하는 자동 구성과 명시적 구성 사이에 충돌이 있는 것 같습니다.아이즈@DataJpaTest이 경우에는 좋은 방법입니다.앱 컨텍스트 구성을 엄격한 JPA 테스트로 제한하고 싶습니다. 이러한 테스트에는 다른 것이 필요하지 않습니다.

간단해야 할 것은...하지만 저는 올바른 방법을 찾을 수 없었고, 이 문제를 해결하는 방법을 명확하게 설명할 수 있는 문서를 찾을 수 없었습니다.

어떤 도움이든 감사합니다!

는 문는에 .@DataJpaTest사용 중입니다.설명서 참조

기본적으로 @DataJpaTest로 주석이 달린 테스트는 내장 메모리 데이터베이스를 사용합니다(명시적이거나 일반적으로 자동으로 구성된 DataSource를 대체함).@AutoConfigureTestDatabase 주석을 사용하여 이러한 설정을 재정의할 수 있습니다.

, 자동 즉 이 자 구 가 정 재 의 되 고 스 소 동 데 성spring.datasource.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS corp되지 않았습니다.

로그에서 유사한 내용을 찾을 수 있습니다.

EmbeddedDataSourceBeanFactoryPostProcessor : Replacing 'dataSource' DataSource bean with embedded version

수정 방법:

spring.test.database.replace=none

솔루션을 요약하자면...@Lesiak의 제안에 따라, 나는 덧붙였습니다.@AutoConfigureTestDatabase 클래스에 을 달아 소스 .@DataJpaTest(자바독에서 명백한 것을 놓친 제가 부끄럽습니다!)이제 테스트 클래스는 다음과 같습니다.

   @RunWith(SpringRunner.class)
   @ContextConfiguration(classes = PersistenceTestConfig.class)
   @DataJpaTest
   @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE)
   @Sql(executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD, scripts = {"classpath:init.sql"})
   public class MyRepoTest {
       ...
    }

컨텍스트 구성:

 @EnableJpaRepositories({"com.mycompany.myproject"})
 @EntityScan({"com.mycompany.myproject"})
 public class PersistenceTestConfig {

     public static void main(String... args) {
          SpringApplication.run(PersistenceTestConfig.class, args);
     }

}

나의application.propertiestest/resources:

  spring.jpa.hibernate.ddl-auto=none

  # adding this line seems to make no difference (perhaps, it targets the default DS, not the one used by Liquibase and tests), but using @Sql to execute 'use corp;' statement before tests works!
  # spring.jpa.properties.hibernate.default_schema=corp

  # LIQUIBASE (LiquibaseProperties)
  spring.liquibase.change-log=classpath:db.changelog.xml
  spring.liquibase.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS corp
  spring.liquibase.user=sa
  spring.liquibase.password=
  spring.liquibase.default-schema=CORP
  #spring.liquibase.liquibase-tablespace=CORP
  spring.liquibase.drop-first=true

init.sql스크립트 상주 위치/test/resources다음과 같은 단일 행이 포함됩니다.use corp;(일부 JPA 엔티티는 카탈로그에 명시적으로 매핑되어 있고 일부는 그렇지 않지만 테스트에서는 모두 동일한 스키마에서 찾아야 하기 때문에 중요합니다.)

Liquibase 작업이 성공했습니다. 로그에서 확인할 수 있습니다.CORP스키마가 생성됩니다 - 모든 테이블 등과 함께.다음을 제외하고는@Sql다음을 가리키는 주석use corp;스크립트, 테스트가 시작되지만 Spring-Data-JPA에서 생성된 쿼리에서만 문제가 없는 것 같습니다.corp.표의 접두사즉, 명시적으로 지정된 카탈로그가 있는 테이블에 매핑된 엔티티 클래스에 대해 쿼리가 생성될 때:@Table(name="my_table", catalog="corp")테스트에서 "corp" 카탈로그에 명시적으로 매핑되지 않은 엔티티를 사용하려고 하면 테이블을 찾을 수 없음을 나타내는 SQL 예외가 느려집니다. 마치 다른 기본 스키마에서 테이블을 찾는 것 같습니다.그래서, 저는 다음을 추가했습니다.@Sql테스트 클래스에 대한 주석(위에 표시된 바와 같이)을 실행합니다.use corp;시험 전 진술.그것이 그 일을 했습니다.(참고: 추가spring.jpa.properties.hibernate.default_schema=corp구성에 아무런 영향을 미치지 않는 것 같습니다.)

@Lesiak, 당신의 도움에 감사드립니다!

스키마 이름은 DB 이름과 같아야 합니다.

이 경우 정답은 다음과 같습니다.

spring.liquibase.url=jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;INIT=CREATE SCHEMA IF NOT EXISTS testdb

spring.liquibase.default-schema=testdb

언급URL : https://stackoverflow.com/questions/57153091/using-liquibase-to-initialize-in-memory-h2-for-unit-tests-in-spring-boot-applica

반응형