Spring @Sql 注解,可以在所有测试之前运行一次吗?

问题描述 投票:0回答:2

使用 Spring 进行集成测试,我可以填充运行脚本的测试数据库,如下所示...

@Test
@Sql({"/db/schema.sql", "/db/accountConfig.sql", "/db/functions/fnSomething.sql"})
public void verifySomething() {
   ...
}

但是,我只想在任何测试运行之前运行所有

.sql
文件一次。 JUnit 4 是否有方法可以做到这一点? 看起来
@Sql
只针对带有 @Test 注释的方法运行。

我正在使用 Junit 4、Spring Boot、Java 15、Testcontainers。

我尝试过的事情...

  • 我尝试在我的测试类扩展的类上使用
    @BeforeClass
    ,但这似乎在我的测试之后运行。
  • Testcontainers 确实有一个初始化脚本功能,但它只需要一个文件,不太理想。
  • 我也尝试过
    ScriptUtils.executeSqlScript
    ,但由于某种原因,测试容器不喜欢这样。

这是我的示例代码,适用于

@Sql
但不适用于
ScriptUtils.executeSqlScript

@ContextConfiguration(initializers = AbstractIntegrationTest.Initializer.class)
public abstract class AbstractIntegrationTest {

    @ClassRule 
    public static MSSQLServerContainer mssqlserver = new MSSQLServerContainer();

  
    public static class Initializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {

        @Override
        public void initialize(ConfigurableApplicationContext configurableApplicationContext) {
            ConfigurableEnvironment environment = configurableApplicationContext.getEnvironment();
            Properties props = new Properties();
            props.put("spring.datasource.driver-class-name", mssqlserver.getDriverClassName());
            props.put("spring.datasource.url", mssqlserver.getJdbcUrl());
            props.put("spring.datasource.username", mssqlserver.getUsername());
            props.put("spring.datasource.password", mssqlserver.getPassword());

            environment
                .getPropertySources()
                .addFirst(new PropertiesPropertySource("myTestDBProps", props));      

            configurableApplicationContext.setEnvironment(environment);
    }
}

我的测试课程只是扩展了

AbstractIntegrationTest
。但是使用
@Sql
可以为每个测试用例运行脚本。有人建议更好的方式来初始化 SQL 脚本吗? 尝试了 Flyway,但它不允许从脚本创建数据库。

java spring spring-boot junit4 testcontainers
2个回答
11
投票

我建议查看在 Spring 上下文初始化时执行一次 SQL 脚本的数据库初始化程序 bean。基本上,有两种解决方案,具体取决于您使用 JDBC 还是 R2DBC。由于您想要初始化多个脚本,因此您应该使用

CompositeDatabasePopulator
。还要记住导入正确的类,因为它们具有相同的名称,但来自不同的包,同样取决于 JDBC/R2DBC。

要从

resources
文件夹加载资源,请随意使用以下选项之一:

  • Resource resource = new ClassPathResource("sql/schema.sql"))
  • @Value("classpath:sql/schema.sql") Resource resource

这个解决方案相当灵活,因为您可以使用

@TestConfiguration
为测试上下文定义初始化程序 bean(请记住,使用此注释有点棘手,所以我建议您参考这篇文章:Spring 的 @TestConfiguration 的怪癖,它帮助我很多)。

该解决方案应适用于任何具有

ConnectionFactory
可用的解决方案,包括 测试容器

JDBC

通过调用

populate
进行的初始化必须在
@PostConstuct
中进行,因为 Spring Boot 自动不会检测到填充器。我建议将以下代码片段包装在配置类中并将其包含在测试范围中。

@Autowired
private DataSource dataSource;

@PostConstruct
public void initData() throws SQLException {

    var populator = new CompositeDatabasePopulator();
    populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("sql/schema.sql")));
    populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("sql/catalog.sql")));
    populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("sql/data.sql")));

    populator.populate(dataSource.getConnection());
}

R2DBC

您可以使用

ConnectionFactoryInitializer
在加载 Spring 上下文时初始化添加到此初始值设定项的所有填充器。

@Bean
public ConnectionFactoryInitializer initializer(ConnectionFactory connectionFactory) {

    var populator = new CompositeDatabasePopulator();
    populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("sql/schema.sql")));
    populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("sql/catalog.sql")));
    populator.addPopulators(new ResourceDatabasePopulator(new ClassPathResource("sql/data.sql")));


    var initializer = new ConnectionFactoryInitializer();
    initializer.setConnectionFactory(connectionFactory);
    initializer.setDatabasePopulator(populator);

    return initializer;
}

0
投票

您可以通过在 INSERT 语句之前添加 DELETE 语句来解决此问题。 像

DELETE FROM table_name WHERE ID IN (Id1, Id2)

之类的东西
© www.soinside.com 2019 - 2024. All rights reserved.