【问题标题】:Spring Boot Test: execute different sql scripts in tests depending on the active profile?Spring Boot Test:根据活动配置文件在测试中执行不同的 sql 脚本?
【发布时间】:2026-01-04 02:10:02
【问题描述】:

Spring Boot Test 是否可以根据活动配置文件设置 sql 脚本的条件执行? 我的意思是,我的存储库集成测试使用了一些 @sql 注释,例如:

@Sql(scripts = "/scripts/entity_test_clear.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
  • 对于配置文件 h2,我想执行 entity_test_clear.sql

  • 对于配置文件 mysql,我想执行 entity_test_clear_mysql.sql

原因是我对这些数据库使用了不同的语法,尤其是这个:

  • ALTER TABLE organisation ALTER COLUMN org_id RESTART WITH 1;
  • ALTER TABLE organisation AUTO_INCREMENT = 1;

mysql不理解语法#1,而h2不理解语法#2(尽管设置了mysql模式,比如MODE=MYSQL)

默认情况下,我使用 h2 进行 IT 测试,但在极少数情况下,我也希望使用 mysql 检查一切是否正常。

PS 我当然可以尝试使用@Profile 的直接解决方案,并为 h2 和 mysql 的每个测试硬编码两个副本,但它与测试中的大量代码重复相结合,我想避免这种情况。

已编辑: 测试用例如下所示:

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(replace= AutoConfigureTestDatabase.Replace.NONE)
public class EntityRepositoryTestIT {

    @Autowired
    private EntityRepository entityRepository;

@Test
@Sql(scripts = {"/scripts/entity_test_data.sql", "/scripts/entity_test_data_many.sql"}, executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD)
@Sql(scripts = "/scripts/entity_test_clear.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)
public void findTest() {
    Page<Entity> e = entityRepository.findBySomeDetails(1L, PageRequest.of(0, 20));
    Assert.assertEquals(3, e.getContent().size());
    Assert.assertEquals(1, e.getContent().get(0).getResources().size());
// more asserts

}

感谢您的任何建议!

【问题讨论】:

  • 你能告诉我们你是如何对 h2 或 MySQL 之一进行测试的
  • 添加了测试用例
  • 你为什么不使用@Profile 和分离的类,把公共逻辑放在另一个类中?
  • 好的,这似乎是一种解决方案!

标签: java sql spring spring-boot


【解决方案1】:

您可以将@Profile 注释与单独的类一起使用,每个类用于每个 DMBS,将公共逻辑放在另一个类中以避免代码重复。您正在使用 Spring,因此您可以通过以下方式获得它。

@Profile("mysql")
@Sql(scripts="... my mysql scripts...")
public class MySqlTests{

    @Autowired
    private CommonTestsLogic commonLogic;

    @Test
    public void mySqlTest1(){
       commonlogic.test1();
    }

}

@Profile("oracle")
@Sql(scripts="... my oracle scripts...")
public class MyOracleTests{

   @Autowired
   private CommonTestsLogic commonLogic;

   @Test
   public void myOracleTest1(){
       commonlogic.test1();
   }

}

【讨论】:

  • 我认为这是一种相当费力的方法,因为我有数百个 IT 测试要像这样重写,而且这些测试看起来很奇怪
【解决方案2】:

在深入研究了这个问题后,我最终得到了这个简单的解决方法。

@Sql(scripts = "/scripts/entity_test_clear.sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)

scripts 参数,必须是编译时间常数。您不能简单地从 application.properties 中获取当前配置文件值并将其替换为运行正确的脚本名称。

在 ScriptUtils 中引入 @After 和 @Before 方法来执行正确的脚本相当冗长,事实上,对我来说不起作用(在脚本执行过程中发生了一些冻结)。

所以我所做的只是引入一个具有单个常量的类:

/**
 * Constant holder for exceptionally database IT testing purposes
 * for switching between h2 and mysql
 */
public class ActiveProfile {
    /**
     * Current profile for database IT tests.
     * Make sure the value is equal to the value of
     * <i>spring.profiles.active</i> property from test application.properties
     */
    public static final String NOW = "h2";
}

那么@sql这一行就变成了:

@Sql(scripts = "/scripts/test_data_clear_"+ ActiveProfile.NOW+".sql", executionPhase = Sql.ExecutionPhase.AFTER_TEST_METHOD)

要使用另一个数据库进行测试 (mysql),我只需要 1) 更改 application.properties 中的当前 spring.profiles.active=mysql 配置文件和 2) 将此常量更改为 mysql;

这并不意味着是典型的解决方案,只是一种简单有效的解决方法。

【讨论】:

    最近更新 更多