SpringBoot 통합 테스트에서 TestContainers로 데이터베이스 채우기
Test Containers를 테스트하고 있는데 .sql 파일을 실행하는 데이터베이스를 채우는 방법을 알고 싶습니다.
어떻게 하는 거야?
@Rule
public PostgreSQLContainer postgres = new PostgreSQLContainer();
가장 쉬운 방법은JdbcDatabaseContainer::withInitScript
이 솔루션의 장점은 스크립트가 실행되기 전에Spring Application Context
(적어도 정적 블록에 있는 경우) 로딩되며 코드는 매우 단순합니다.
예:
static {
postgreSQLContainer = new PostgreSQLContainer("postgres:9.6.8")
.withDatabaseName("integration-tests-db")
.withUsername("sa")
.withPassword("sa");
postgreSQLContainer
.withInitScript("some/location/on/classpath/someScript.sql");
postgreSQLContainer.start();
}
JdbcDatabaseContainer
의 슈퍼클래스이다PostgreSQLContainer
따라서 이 솔루션은 다음 고객만을 대상으로 하는 것이 아닙니다.postgres
, 다른 컨테이너에 대해서도 마찬가지입니다.
여러 스크립트를 실행하는 경우에도 동일한 방법으로 실행할 수 있습니다.
예:
static {
postgreSQLContainer = new PostgreSQLContainer("postgres:9.6.8")
.withDatabaseName("integration-tests-db")
.withUsername("sa")
.withPassword("sa");
postgreSQLContainer.start();
var containerDelegate = new JdbcDatabaseDelegate(postgreSQLContainer, "");
ScriptUtils.runInitScript(containerDelegate, "some/location/on/classpath/someScriptFirst.sql");
ScriptUtils.runInitScript(containerDelegate, "some/location/on/classpath/someScriptSecond.sql");
ScriptUtils.runInitScript(containerDelegate, "ssome/location/on/classpath/someScriptThird.sql");
}
다른 옵션도 있습니다.
스프링 테스트@Sql
주석
@SpringBootTest
@Sql(scripts = ["some/location/on/classpath/someScriptFirst.sql", "some/location/on/classpath/someScriptSecond.sql"])
public class SomeTest {
//...
}
ResourceDatabasePopulator
부터jdbc.datasource.init
또는r2dbc.connection.init
사용할 때JDBC
또는R2DBC
연속해서
class DbInitializer {
private static boolean initialized = false;
@Autowired
void initializeDb(ConnectionFactory connectionFactory) {
if (!initialized) {
ResourceLoader resourceLoader = new DefaultResourceLoader();
Resource[] scripts = new Resource[] {
resourceLoader.getResource("classpath:some/location/on/classpath/someScriptFirst.sql"),
resourceLoader.getResource("classpath:some/location/on/classpath/someScriptSecond.sql"),
resourceLoader.getResource("classpath:some/location/on/classpath/someScriptThird.sql")
};
new ResourceDatabasePopulator(scripts).populate(connectionFactory).block();
initialized = true;
}
}
}
@SpringBootTest
@Import(DbInitializer.class)
public class SomeTest {
//...
}
사용할 때 데이터베이스 URI에서 스크립트 초기화JDBC
그것은 공식에 언급되어 있다.Testcontainers
문서:
https://www.testcontainers.org/modules/databases/jdbc/
클래스 경로 파일:
jdbc:tc:postgresql:9.6.8:///databasename?TC_INITSCRIPT=somepath/init_mysql.sql
클래스 경로에 없지만 일반적으로 프로젝트 루트가 되는 작업 디렉터리에 상대적인 파일:
jdbc:tc:postgresql:9.6.8:///databasename?TC_INITSCRIPT=file:src/main/resources/init_mysql.sql
init 함수 사용:
jdbc:tc:postgresql:9.6.8:///databasename?TC_INITFUNCTION=org.testcontainers.jdbc.JDBCDriverTest::sampleInitFunction
package org.testcontainers.jdbc;
public class JDBCDriverTest {
public static void sampleInitFunction(Connection connection) throws SQLException {
// e.g. run schema setup or Flyway/liquibase/etc DB migrations here...
}
...
}
Spring Boot을 사용할 때는 Test Containers의 JDBC URL 지원을 가장 쉽게 사용할 수 있습니다.
작성할 수 있습니다.application-integration-test.properties
파일(일반적으로src/test/resources
다음과 같은 방법으로요.
spring.datasource.url=jdbc:tc:postgresql://localhost/myappdb
spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver
spring.datasource.username=user
spring.datasource.password=password
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect
spring.jpa.hibernate.ddl-auto=none
# This line is only needed if you are using flyway for database migrations
# and not using the default location of `db/migration`
spring.flyway.locations=classpath:db/migration/postgresql
주의::tc
JDBC URL의 일부입니다.
이제 다음과 같이 단위 테스트를 작성할 수 있습니다.
@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @ActiveProfiles("integration-test")
public class UserRepositoryIntegrationTest {
@Autowired
private MyObjectRepository repository;
@PersistenceContext
private EntityManager entityManager;
@Autowired
private JdbcTemplate template;
@Test
public void test() {
// use your Spring Data repository, or the EntityManager or the JdbcTemplate to run your SQL and populate your database.
}
주의: 이는 Spring Boot에 의한 API 백엔드 구축 실무 가이드, 7장(해임자:저는 이 책의 저자입니다.)
스프링 프레임워크는 테스트 스위트 또는 테스트 유닛에 대해 SQL 스크립트를 실행할 수 있는 기능을 제공합니다.예를 들어 다음과 같습니다.
@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"})
public void userTest {
// execute code that relies on the test schema and test data
}
여기 서류입니다.
스프링 테스트 DBUnit에서는 테스트 유닛의 데이터베이스를 채울 수 있는 주석을 제공합니다.XML 데이터 집합 파일을 사용합니다.
@Test
@DatabaseSetup(value = "insert.xml")
@DatabaseTearDown(value = "insert.xml")
public void testInsert() throws Exception {
// Inserts "insert.xml" before test execution
// Remove "insert.xml" after test execution
}
또한 데이터베이스를 채우기 위한 Java fluent DSL을 제공하는 DbSetup을 볼 수 있습니다.
Postgres 컨테이너를 고급 테스트 컨테이너 없이 수동으로 정의하는 경우 스프링과 직접 관련이 없는 JDBC URL 항목이 하나 더 있습니다.Postgres 이미지를 사용하면 SQL 스크립트를 포함하는 디렉토리를 컨테이너 볼륨에 링크하여 자동으로 실행할 수 있습니다.
GenericContainer pgDb = new PostgreSQLContainer("postgres:9.4-alpine")
.withFileSystemBind("migrations/sqls", "/docker-entrypoint-initdb.d",
BindMode.READ_ONLY)
또한 런타임에 필요한 것이 있으면 언제든지 할 수 있습니다.pgDb.execInContainer("psql ....")
.
DBUnit을 백그라운드에서 사용하는 DatabaseRider를 사용하여 테스트 데이터베이스 및 TestContainer를 테스트 데이터 소스로 채울 수 있습니다.다음은 샘플 테스트입니다.전체 소스 코드는 여기 github에서 확인할 수 있습니다.
@RunWith(SpringRunner.class)
@SpringBootTest
@DataJpaTest
@AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) @ActiveProfiles("integration-test")
@DBRider //enables database rider in spring tests
@DBUnit(caseInsensitiveStrategy = Orthography.LOWERCASE) //https://stackoverflow.com/questions/43111996/why-postgresql-does-not-like-uppercase-table-names
public class SpringBootDBUnitIt {
private static final PostgreSQLContainer postgres = new PostgreSQLContainer(); //creates the database for all tests on this file
@PersistenceContext
private EntityManager entityManager;
@Autowired
private UserRepository userRepository;
@BeforeClass
public static void setupContainer() {
postgres.start();
}
@AfterClass
public static void shutdown() {
postgres.stop();
}
@Test
@DataSet("users.yml")
public void shouldListUsers() throws Exception {
assertThat(userRepository).isNotNull();
assertThat(userRepository.count()).isEqualTo(3);
assertThat(userRepository.findByEmail("springboot@gmail.com")).isEqualTo(new User(3));
}
@Test
@DataSet("users.yml") //users table will be cleaned before the test because default seeding strategy
@ExpectedDataSet("expected_users.yml")
public void shouldDeleteUser() throws Exception {
assertThat(userRepository).isNotNull();
assertThat(userRepository.count()).isEqualTo(3);
userRepository.delete(userRepository.findOne(2L));
entityManager.flush();//can't SpringBoot autoconfigure flushmode as commit/always
//assertThat(userRepository.count()).isEqualTo(2); //assertion is made by @ExpectedDataset
}
@Test
@DataSet(cleanBefore = true)//as we didn't declared a dataset DBUnit wont clear the table
@ExpectedDataSet("user.yml")
public void shouldInsertUser() throws Exception {
assertThat(userRepository).isNotNull();
assertThat(userRepository.count()).isEqualTo(0);
userRepository.save(new User("newUser@gmail.com", "new user"));
entityManager.flush();//can't SpringBoot autoconfigure flushmode as commit/always
//assertThat(userRepository.count()).isEqualTo(1); //assertion is made by @ExpectedDataset
}
}
src/test/resources/application-integration-test.properties
spring.datasource.url=jdbc:tc:postgresql://localhost/test
spring.datasource.driverClassName=org.testcontainers.jdbc.ContainerDatabaseDriver
spring.datasource.username=test
spring.datasource.password=test
spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect
spring.jpa.hibernate.ddl-auto=create
spring.jpa.show-sql=true
#spring.jpa.properties.org.hibernate.flushMode=ALWAYS #doesn't take effect
spring.jpa.hibernate.naming-strategy=org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect
마지막으로 데이터셋:
src/test/resources/datasets/users.yml
users:
- ID: 1
EMAIL: "dbunit@gmail.com"
NAME: "dbunit"
- ID: 2
EMAIL: "rmpestano@gmail.com"
NAME: "rmpestano"
- ID: 3
EMAIL: "springboot@gmail.com"
NAME: "springboot"
src/test/resources/datasets/expected_users.yml
users:
- ID: 1
EMAIL: "dbunit@gmail.com"
NAME: "dbunit"
- ID: 3
EMAIL: "springboot@gmail.com"
NAME: "springboot"
src/test/resources/datasets/user.yml
users:
- ID: "regex:\\d+"
EMAIL: "newUser@gmail.com"
NAME: "new user"
몇 가지 검토 후, 테스트 컨테이너를 사용하는 Spring Data JDBC의 예를 검토하는 것이 흥미롭다고 생각합니다.
주의: Java 8 사용
git clone https://github.com/spring-projects/spring-data-jdbc.git
mvn clean install -Pall-dbs
이전 프로젝트에 대한 아이디어를 추가하여 간단한 프로젝트를 만들겠습니다.
후안 안토니오
언급URL : https://stackoverflow.com/questions/53078306/populate-a-database-with-testcontainers-in-a-springboot-integration-test
'prosource' 카테고리의 다른 글
Angular UI-Router "레이아웃" 상태를 만드는 방법 (0) | 2023.03.14 |
---|---|
JDBC 문을 통해 "DDL 실행 오류 "변경 테이블 이벤트 드롭 외부 키 FKg0mkvgsqn8584qoql6a2rxheq" 수정 방법 (0) | 2023.03.09 |
WordPress는 로그인한 사용자에게 개인 투고를 표시합니다. 이 기능을 해제하려면 어떻게 해야 합니까? (0) | 2023.03.09 |
표준 json 모듈에서는 포맷이 플로팅됩니다. (0) | 2023.03.09 |
Jackson이 Getter를 @JsonProperty로 덮어쓰지 않음 (0) | 2023.03.09 |