Spring boot 在SpringBoot集成测试中使用TestContainers填充数据库

Spring boot 在SpringBoot集成测试中使用TestContainers填充数据库,spring-boot,testcontainers,Spring Boot,Testcontainers,我正在测试TestContainers,我想知道如何通过执行.sql文件来填充数据库,以创建结构并添加一些行 怎么做 @Rule public PostgreSQLContainer postgres = new PostgreSQLContainer(); 使用SpringBoot时,我发现使用TestContainers的JDBCURL支持最简单 您可以创建一个应用程序集成test.properties文件(通常位于src/test/resources中),如下所示: spring.dat

我正在测试TestContainers,我想知道如何通过执行.sql文件来填充数据库,以创建结构并添加一些行

怎么做

@Rule
public PostgreSQLContainer postgres = new PostgreSQLContainer();

使用SpringBoot时,我发现使用TestContainers的JDBCURL支持最简单

您可以创建一个
应用程序集成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
@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.
}
注意JDBC url中的
:tc
部分

现在可以编写如下的单元测试:

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
@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.
}

注意:第7章对此进行了更详细的解释(免责声明:我是本书的作者)

Spring framework提供了为测试套件或测试单元执行SQL脚本的能力。例如:

@Test
@Sql({"/test-schema.sql", "/test-user-data.sql"}) 
public void userTest {
   // execute code that relies on the test schema and test data
}
这是我的建议

您还可以查看为测试单元填充数据库所提供的注释。它使用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来填充您的数据库。

您可以使用,它在后台用于填充测试数据库并作为测试数据源。下面是一个示例测试,上提供了完整的源代码

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/dataset/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"
users:
  - ID: 1
    EMAIL: "dbunit@gmail.com"
    NAME: "dbunit"
  - ID: 3
    EMAIL: "springboot@gmail.com"
    NAME: "springboot"
users:
  - ID: "regex:\\d+"
    EMAIL: "newUser@gmail.com"
    NAME: "new user"
src/test/resources/dataset/expected_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"
users:
  - ID: 1
    EMAIL: "dbunit@gmail.com"
    NAME: "dbunit"
  - ID: 3
    EMAIL: "springboot@gmail.com"
    NAME: "springboot"
users:
  - ID: "regex:\\d+"
    EMAIL: "newUser@gmail.com"
    NAME: "new user"
src/test/resources/dataset/user.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"
users:
  - ID: 1
    EMAIL: "dbunit@gmail.com"
    NAME: "dbunit"
  - ID: 3
    EMAIL: "springboot@gmail.com"
    NAME: "springboot"
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
我将创建一个简单的项目,添加一些关于以前引用的项目的想法


Juan Antonio

如果您手动定义Postgres容器,而不使用高级testcontainers JDBC url,那么还有一个选项,它与Spring没有直接关系。Postgres映像允许将包含sql脚本的目录链接到容器卷并自动执行它们

GenericContainer pgDb = new PostgreSQLContainer("postgres:9.4-alpine")
  .withFileSystemBind("migrations/sqls", "/docker-entrypoint-initdb.d",
    BindMode.READ_ONLY)
如果您在运行时需要一些东西,您也可以随时这样做
pgDb.execInContainer(“psql….”)

最简单的方法是使用
JdbcDatabaseContainer::withInitScript
此解决方案的优点是,脚本在加载
Spring应用程序上下文之前运行(至少在静态块中时),代码非常简单

例如:

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();
}
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");
}
JdbcDatabaseContainer
postgresqlcainer
的超类,因此此解决方案不仅适用于
postgres
,也适用于其他容器

如果你想运行多个脚本,你可以用类似的方式 例如:

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();
}
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
注释
ResourceDatabasePopulator
from
jdbc.datasource.init
r2dbc.connection.init
连续使用
jdbc
r2dbc
时 使用
JDBC时数据库URI中的Init脚本
它在官方
Testcontainers
文档中提到:

类路径文件:
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

使用初始化函数:
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...
    }
    ...
}

您是否只使用JUnit和TestContainers?或者也使用其他框架,例如Spring Boot?您好,我在Spring Boot环境中使用此库您可以在文档中找到:还有一篇文章提到了
PostgreSQLContainer::withInitScript
方法的用法:实际上它是
JdbcDatabaseContainer::withInitScript
其中
JdbcDatabaseContainer
postgresqlcainer
的超类,因此它不仅适用于postgres,也适用于其他容器。您好,Wim,感谢您的回复。有趣的答案是,对于TestContainers,不需要重新发明轮子,因此基本上如果我们有数据库,那么我们只需要o继续使用Spring Boot的解决方案。我明天将进行测试,我将给你投票:)祝贺你有了这本参考书。明天我会下载并复习。BrusselsI对此表示欢迎,但我想知道如何为每个集成类配置一个新的容器me@CHEM_Eugene您是否可以在您发布的代码中添加一个文件示例?不确定
“/docker entrypoint initdb.d/init postgres.sql”的位置
应该来自哪里?这可能会覆盖image的
/docker entrypoint initdb.d
,如果它已经设置好了,您如何在处理复杂数据库时添加这样的SQL并需要维护外键关系?
@SQL
对我来说很好