无法为 spring 引导中的每个测试方法初始化数据库

Unable to initialize DB for every test method in spring boot

我正在尝试为 spring 启动设置一个集成测试场景,其中为每个测试方法创建并使用自定义 sql 代码初始化新的 H2 数据库。

从文档中我了解到我所要做的就是添加

@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)

我的测试class。

我可以从日志中看到,这确实启动了多个应用程序上下文而不是一个。

但它接缝,这个上下文在任何测试之前初始化 运行 并且实际上同时存在于 JVM 中。但我认为他们共享一个 H2 实例。 sql init 脚本第一次执行得很好,但随后出现 Table already exists 错误,因为它试图创建已经存在的表。

我如何确保测试,包括 spring 应用程序上下文和 H2 DB 是完全序列化的?

application.properties

server.port=8001

spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.url=jdbc:h2:mem:db;DB_CLOSE_DELAY=-1
spring.datasource.username=sa
spring.datasource.password=sa
spring.jpa.show-sql=true
spring.jpa.generate-ddl=false
spring.jpa.hibernate.ddl-auto=none
spring.datasource.schema=classpath*:h2/ddl/infop-schemas.sql, \
  classpath*:h2/ddl/infop-tables-fahrplan.sql, \
  classpath*:h2/ddl/infop-tables-import.sql, \
  classpath*:h2/ddl/infop-tables-stammdaten.sql, \
  classpath*:h2/ddl/infop-tables-statistik.sql, \
  classpath*:h2/ddl/infop-tables-system.sql, \
  classpath*:h2/ddl/infop-tables-utility.sql, \
  classpath*:h2/ddl/infop-sequences.sql, \
  classpath*:h2/ddl/infop-views.sql \
  classpath*:h2/dll/infop-constraints-system.sql \
  classpath*:h2/dll/infop-constraints-stammdaten.sql \
  classpath*:h2/dll/infop-constraints-statistik.sql \
  classpath*:h2/dll/infop-constraints-import.sql \
  classpath*:h2/dll/infop-constraints-fahrplan.sql

测试class:

@ExtendWith(SpringExtension.class)
@SpringBootTest(classes = {TestApplicationDao.class})
@ActiveProfiles("test")
@Transactional
@DirtiesContext(classMode = DirtiesContext.ClassMode.BEFORE_EACH_TEST_METHOD)
public class ProtokollIntegrationTest {

    private static final Logger LOGGER = LoggerFactory.getLogger(ProtokollIntegrationTest.class);

    @Test
    public void thatMaxLaufnummerIsFound() {
        LOGGER.debug("thatMaxLaufnummerIsFound()");
        Optional<Protokoll> maxProtokollOpt = protokollRepository.findFirstByAuftragSchrittOrderByLaufnummerDesc(auftragSchritt);

        assertTrue(maxProtokollOpt.isPresent());
        assertEquals(new Integer(9), maxProtokollOpt.get().getLaufnummer());
    }

    @Test
    public void thatNoLaufnummerIsFound() {
        LOGGER.debug("thatNoLaufnummerIsFound()");
        AuftragSchritt as = new AuftragSchritt();
        as.setStatusCode(code);
        auftragSchrittRepository.save(as);

        Optional<Protokoll> maxProtokollOpt = protokollRepository.findFirstByAuftragSchrittOrderByLaufnummerDesc(as);

        assertFalse(maxProtokollOpt.isPresent());
    }

    @Test
    public void thatFindByAuftragSchrittWorksFine() {
        LOGGER.debug("thatFindByAuftragSchrittWorksFine()");
        List<Protokoll> protokollList = protokollRepository.findByAuftragSchritt(auftragSchritt);

        assertNotNull(protokollList);
        assertEquals(10, protokollList.size());
    }

}

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.compay.my-prj</groupId>
    <artifactId>my-prj-dao</artifactId>
    <version>0.2.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.4.RELEASE</version>
    </parent>

    <properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <maven.build.timestamp.format>yyyy-MM-dd HH:mm</maven.build.timestamp.format>
        <timestamp>${maven.build.timestamp}</timestamp>
        <junit.jupiter.version>5.4.2</junit.jupiter.version>
        <junit.platform.launcher.version>1.4.2</junit.platform.launcher.version>
        <my-prj.version>8.25.0</my-prj.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.9</version>
        </dependency>
        <dependency>
            <groupId>com.compay.my-prj</groupId>
            <artifactId>my-prj-common-entity</artifactId>
            <version>${my-prj.version}</version>
            <classifier>hibernate</classifier>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <!-- wir werden junit5 verwenden (unten) -->
                <exclusion>
                    <groupId>junit</groupId>
                    <artifactId>junit</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.compay.my-prj</groupId>
            <artifactId>my-prj-dao-test</artifactId>
            <version>0.5.0</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.compay.my-prj</groupId>
            <artifactId>my-prj-common-entity-test</artifactId>
            <version>${my-prj.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>${junit.jupiter.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-launcher</artifactId>
            <version>${junit.platform.launcher.version}</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-source-plugin</artifactId>
                <executions>
                    <execution>
                        <id>attach-sources</id>
                        <goals>
                            <goal>jar</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
            <!-- für junit5 -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>2.22.2</version>
            </plugin>
        </plugins>
    </build>
</project>

日志输出见https://1drv.ms/t/s!AnJdkNZlKN5ygVi72qB6wL1KOcpZ (抱歉,它太大了)

在 JPA/Hibernate 的正常 spring.jpa.hibernate.ddl-auto 设置为 create-dropDirtiesContext 设置为 BEFORE_EACH_TEST_METHOD 的情况下,它会像 jpa/hibernate将尝试先删除所有表,并在每次测试用例执行后重新创建所有表。 Create/DROP 由 hibernate 处理的场景(在你的情况下没有发生)

但是根据您的设置,即 DirtiesContext = BEFORE_EACH_TEST_METHODspring.datasource.schema = <multiple-sql-files>,表将由您的脚本创建但不会被删除。因此,您收到 Table already exists 错误。

我建议您再添加一个 SQL 文件 在开头 ,其中包含删除所有已创建 tables/views(如果存在)的查询。它肯定会解决您的问题。

毕竟这是一个缺失的依赖项!

我没有

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-devtools</artifactId>
        <optional>true</optional>
    </dependency>

这会导致自动配置变得疯狂,并以错误的顺序执行诸如初始化应用程序上下文之类的事情以及其他奇怪的事情。糟糕的是,这根本不会发出任何错误或警告消息。

所以学习是:

如果您的代码中有 @DirtiesContext,您必须将 spring-boot-devtools 添加到依赖项中。