在 Spring 上下文之前动态创建 schema.sql

Dynamically creating schema.sql before Spring context

我正在为项目编写集成测试,我想在 Spring 选择它来填充数据库之前将所有数据库迁移脚本合并到 schema.sql 中。 为此,我使用一个小的 class 在项目中搜索 sql 文件并将它们合并为一个。 我创建了一个这样的套件:

@RunWith(Suite.class)
@Suite.SuiteClasses({MyTests.class})
public class SuiteTest {    
    @BeforeClass
    public static void setUp() throws IOException {
        RunMigrations.mergeMigrations();//this one merges all sqls into one file, called schema.sql
    }
}

那么,这是我的测试:

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest(classes = App.class)
@ActiveProfiles(resolver = CustomActiveProfileResolver.class)
@ContextConfiguration(classes = App.class)
public class MyTests extends AbstractTransactionalJUnit4SpringContextTests {
@PostConstruct
    public void before() {
        mvc = MockMvcBuilders.webAppContextSetup(context).addFilter(springSecurityFilterChain).build();
    }    
    @Test
    @Transactional
    public void Test1(){ //do stuff }
}

但这并不像我想象的那样有效。看起来 Spring 尝试 运行 schema.sql 比我创建它的速度更快,但失败了:

Caused by: org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [application]

如果我只是关闭生成 schema.sql 的代码并让 Spring 运行 使用已创建的架构,那么一切都很好。但是,如果我删除 schema.sql 并让我的 class 生成它,那么它就会像描述的那样失败。 我试图在 SpringJUnit4ClassRunner 中覆盖 运行(RunNotifier notifier) 方法并将我的迁移合并放在那里,然后再调用 super.run(notifier) 方法,但这仍然没有工作。 有没有办法在 Spring 得到它之前生成 schema.sql?

P.S。 我不能将 flyway 用于生产环境。也许仅用于测试是可能的?

更新: 经过一些试验,我将其设置为 test.yml:

spring.jpa.hibernate.ddl-auto: none

现在它加载上下文,执行一个只获取 Oauth2 令牌的测试,并在执行 POST 和 GET 请求的其他测试中失败,因为它无法执行在测试之前放置额外数据的 @sql 注释方法。数据库似乎未受影响,即没有任何表格。

您可以通过使用 @TestPropertySource(properties = {"spring.flyway.enabled=true"}) 注释或使用自己的 属性 文件创建 test Spring 配置文件来仅为测试启用飞路。后者看起来像:

@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("test")
public MyTest {

src/test/resources/application-test.yml 文件:

spring:
  flyway:
    enabled: true

flyway-core 作为测试范围的依赖项。

请注意,Spring Boot 中的 Flyway 属性在 Spring Boot 2.0 中已重命名。

也许有人会觉得这很有用。 我设法通过使用带有故障安全插件的 exec-maven-plugin 来解决这个问题。 测试 class 设置保持不变,我只是从 Suite 中删除了 @BeforeClass 注释。 这是 POM:

<build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${org.apache.maven.plugins.maven-surefire-plugin-version}</version>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-failsafe-plugin</artifactId>
                <version>${org.apache.maven.plugins.maven-failsafe-plugin-version}</version>
                <executions>
                    <execution>
                        <id>integration-test-for-postgres</id>
                        <phase>integration-test</phase>
                        <goals>
                            <goal>integration-test</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>verify-for-postgres</id>
                        <phase>verify</phase>
                        <goals>
                            <goal>verify</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>exec-maven-plugin</artifactId>
                <version>${org.codehaus.mojo.exec-maven-plugin-version}</version>
                <executions>
                    <execution>
                        <id>build-test-environment</id>
                        <phase>generate-test-resources</phase>
                        <goals>
                            <goal>java</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <!--This class prepares the schema.sql file which is fed to Spring to init DB before tests.-->
                    <mainClass>...GenerateTestDBSchema</mainClass>
                    <arguments>
                        <argument>...</argument><!--the migration folder-->
                        <argument>...</argument><!--The path where to put schema sql-->
                    </arguments>
                </configuration>
            </plugin>
        </plugins>
    </build>

GenerateTestDBSchema class 具有 main 方法并使用 args 数组接受查找迁移和放置的路径 schema.sql。

public static void main(String[] args) {
        try {
            mergeMigrations(args[0], args[1]);
        } catch (IOException e) {
            LOG.error(e.getMessage());
        }
    }

mergeMigrations() 方法很简单:只需从目录中取出所有文件,合并它们并写入输出路径。这样 Spring 在上下文启动之前就有了 schema.sql 并且它自己决定 运行 迁移到哪里。 感谢@ActiveProfiles(resolver = CustomActiveProfileResolver.class) 在集成测试中,spring 解析配置文件并获取 application-{profileName}.yml 并自动设置数据库地址。