在 DataJPATest 下运行单元测试时出现问题

Posted

技术标签:

【中文标题】在 DataJPATest 下运行单元测试时出现问题【英文标题】:Problem running unit test under DataJPATest 【发布时间】:2020-05-08 02:45:32 【问题描述】:

这是我第一次尝试使用 H2 内存数据库编写 Spring Boot 单元测试。我找到的指南使它看起来很简单,但我无法让它发挥作用。

H2 正在生成一长串语法错误,我相信尝试执行实体所需的所有 DDL。

我的单元测试类:

    @RunWith(SpringRunner.class)
    @DataJpaTest()
    public class AppRepositoryTest
    

        @Autowired
        private TestEntityManager entityManager;

        @Autowired
        private OrganizationRepository organizationRepository;

        @Test
        public void test_findAppsForUserAndTeam()
        
            // given
            Organization organization = new Organization();
            organization.setOrganizationCode("o");
            organization.setOrganizationName("O");
            entityManager.persist(organization);
            entityManager.flush();

            // when
            Organization found = organizationRepository.getOrganizationWithParents("o")
                    .orElseThrow(() -> new RuntimeException("Org not found"));

            // then
            assertThat(found.getOrganizationName(), is("O"));
        
    

我要做的第一件事是创建架构,我将这一行添加到 resources/schema.sql 中:

CREATE SCHEMA  IF NOT EXISTS EUAMDB;

运行测试失败,出现一长串语法错误,比如:

org.hibernate.tool.schema.spi.CommandAcceptanceException: Error executing DDL "drop table euamdb.public.access_requests if exists" via JDBC Statement
    at org.hibernate.tool.schema.internal.exec.GenerationTargetToDatabase.accept(GenerationTargetToDatabase.java:67) ~[hibernate-core-5.4.12.Final.jar:5.4.12.Final]
        ...
Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Syntax error in SQL statement "DROP TABLE EUAMDB.PUBLIC.[*]ACCESS_REQUESTS IF EXISTS"; SQL statement:
drop table euamdb.public.access_requests if exists [42000-200]
    at org.h2.message.DbException.getJdbcSQLException(DbException.java:453) ~[h2-1.4.200.jar:1.4.200]

感谢任何帮助。

更新:

我知道我需要让 Spring 使用 euamdb 作为数据库名称(感谢@Evgenij)。目前它会生成一个随机数据库名称,正如我在日志中看到的那样:

Starting embedded database: url='jdbc:h2:mem:9929f516-5795-477c-8a8a-343b4b30ebf9;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'

如果我在调试器创建数据源之前停止代码,并将随机数据库名称替换为“euamdb”,则测试运行。我只是不知道如何告诉 spring 使用什么作为 H2 数据库名称。

我已尝试更改@DataJpaTest 注释以将属性设置为:

@DataJpaTest(properties = "spring.datasource.url=jdbc:h2:mem:euamdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false")

还尝试添加此注释:

@TestPropertySource(properties = 
        "spring.datasource.url=jdbc:h2:mem:euamdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false")

最后修改了application.yml中的spring.datasource.url,但是这些都没有改变执行设置数据库名的方式。

【问题讨论】:

【参考方案1】:

在像DROP TABLE EUAMDB.PUBLIC.ACCESS_REQUESTS 这样的命令中,EUAMDB 部分是目录的名称(在 H2 中它是数据库的名称),PUBLIC 部分是模式的名称(PUBLIC 是默认值H2 中的模式,而不仅仅是 H2 中的模式),ACCESS_REQUESTS 是表的名称。因此,您不需要 EUAMDB 架构,但您需要将数据库重命名为 EUAMDB。例如,此类名称将与jdbc:h2:mem:euamdbjdbc:h2:./euamdb URL 一起使用。

【讨论】:

谢谢 - 我如何设置它以便单元测试在运行单元测试时使用 H2 数据库名称 euamdb? 您应该在某处拥有 JDBC URL(例如,您的库可以在文件 application.properties 中使用 spring.datasource.url 参数)。您需要使用 euamdb 作为该 URL 中的数据库名称。另一种解决方案是在 URL 中使用IGNORE_CATALOGS=TRUE 参数,或者您可以在应用程序初始化期间的某处执行SET IGNORE_CATALOGS TRUE; 命令:h2database.com/html/commands.html#set_ignore_catalogs 我在 application.yml 中设置了另一个(非内存,postgresql)数据库,但我的理解是 DataJpaTest 注释将其替换为 H2 以运行测试。那么,我在 application.properties 中为 H2 JDBC URL 输入的内容是否重要,这会破坏我的运行时数据库吗?我原以为注释中有某种方法可以做到这一点。 您的项目是如何组织的?它可以有 src/main/resourcessrc/test/resources 目录,其中包含不同的配置文件或类似的东西。 查看我更新的帖子...我只是不知道如何告诉 Spring 使用数据库名称 euamdb - 它总是使用随机生成的数据库名称【参考方案2】:

我解决了这个问题,虽然这个解决方案并没有真正直接回答这个问题。

我确定不需要实体上的 @Table 注释的 catalog = "euamdb" 部分。因此,当所有实体中的那些被删除时,测试就可以运行了。我也没有看到对应用程序有任何影响。

【讨论】:

以上是关于在 DataJPATest 下运行单元测试时出现问题的主要内容,如果未能解决你的问题,请参考以下文章

DataJpaTest 无法在单元测试中更新

如何在使用 DataJpaTest 的 Spring Boot 2.0 测试中访问 H2 控制台

运行 @DataJpaTest Junit 时,正在使用资源和测试资源文件夹中的两个 data.sql 文件

Spring @DataJpaTest 与 JUnit 5

运行单元测试时出现语法错误后 PhantomJS 退出

尝试通过测试单元引用数据库连接时出现 C#.net 异常