Spring Boot DataJpaTest 单元测试恢复到 H2 而不是 mySql

Posted

技术标签:

【中文标题】Spring Boot DataJpaTest 单元测试恢复到 H2 而不是 mySql【英文标题】:Spring Boot DataJpaTest unit test reverting to H2 instead of mySql 【发布时间】:2019-03-30 19:56:31 【问题描述】:

我有一个简单的小“hello world”Spring Boot 应用程序。它有一个实体(“IssueReport”),并配置为运行 mysql(而不是默认的 H2 嵌入式数据库)。

应用程序本身运行良好。我创建了一个 mySql 数据库和用户,Spring Boot/Hibernate 创建了表,并在我运行应用程序时成功填充和读取了 mySQL 数据。生活是美好的 - mySQL 和我的 Spring Boot 应用程序本身没有问题。

问:现在如何在单元测试中使用 mySQL(而不是嵌入式 H2)?

    我创建了第二个独立的 mySQL 数据库:test2_test_db

    我使用的是 Spring Boot 2.0.6; STS 3.9.6 上的 Eclipse Photon; Ubuntu Linux。

    我在src/test/resources/ 中创建了application-test.properties

    spring.datasource.url=jdbc:mysql://localhost:3306/test2_test_db
    spring.datasource.username=springuser
    spring.datasource.password=springuser
    spring.jpa.hibernate.ddl-auto=create
    

    这是整个单元测试:

    package com.hellospring.example;
    
    import static org.assertj.core.api.Assertions.assertThat;
    import java.util.List;
    import javax.transaction.Transactional;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
    import org.springframework.boot.test.autoconfigure.orm.jpa.TestEntityManager;
    import org.springframework.test.context.ActiveProfiles;
    import org.springframework.test.context.junit4.SpringRunner;
    import com.hellospring.example.entity.IssueReport;
    import com.hellospring.example.repositories.IssueRepository;
    
    @RunWith(SpringRunner.class)
    @ActiveProfiles("test")
    @Transactional
    @DataJpaTest
    public class IssueRepositoryIntegrationTests 
    
         @Autowired
         private TestEntityManager entityManager;
    
         @Autowired
         private IssueRepository issueRepository;
    
         @Test
         public void addNewIssue() 
             System.out.println("addNewIssue()...");  // <-- This prints in the console
             final String email = "test@test.io";
             List<IssueReport> resultSet = issueRepository.findAll();  // <-- We get an exception in here...
           
    
    

    这是控制台错误:

    2018-10-25 22:20:16.381  INFO 13637 --- [           main] c.v.e.IssueRepositoryIntegrationTests    : The following profiles are active: test
    2018-10-25 22:20:16.405  INFO 13637 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@d554c5f: startup date [Thu Oct 25 22:20:16 PDT 2018]; root of context hierarchy
    2018-10-25 22:20:17.059  INFO 13637 --- [           main] beddedDataSourceBeanFactoryPostProcessor : Replacing 'dataSource' DataSource bean with embedded version
    2018-10-25 22:20:17.060  INFO 13637 --- [           main] o.s.b.f.s.DefaultListableBeanFactory     : Overriding bean definition for bean 'dataSource' with a different definition: replacing [Root bean: class [null]; scope=; abstract=false; lazyInit=false; autowireMode=3; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration$Hikari; factoryMethodName=dataSource; initMethodName=null; destroyMethodName=(inferred); defined in class path resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]] with [Root bean: class [org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration$EmbeddedDataSourceFactoryBean]; scope=; abstract=false; lazyInit=false; autowireMode=0; dependencyCheck=0; autowireCandidate=true; primary=false; factoryBeanName=null; factoryMethodName=null; initMethodName=null; destroyMethodName=null]
    2018-10-25 22:20:17.308  INFO 13637 --- [           main] o.s.j.d.e.EmbeddedDatabaseFactory        : Starting embedded database: url='jdbc:h2:mem:979b3ce9-604e-4efd-a6d4-79576c3d67e9;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=false', username='sa'
    2018-10-25 22:20:17.685  INFO 13637 --- [           main] j.LocalContainerEntityManagerFactoryBean : Building JPA container EntityManagerFactory for persistence unit 'default'
      ...  <= I do *NOT* want H2!  I want mySQL!
    
    2018-10-25 22:20:19.315  WARN 13637 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : SQL Error: 42102, SQLState: 42S02
    2018-10-25 22:20:19.316 ERROR 13637 --- [           main] o.h.engine.jdbc.spi.SqlExceptionHelper   : Table "ISSUE_REPORT" not found; SQL statement:
      ...  <= Here's the exception from running the test...
    

问:我可以使用 mySQL 运行单元测试,就像我可以使用 mySQL 运行 Spring Boot 应用程序一样,最简单的更改是什么?

问:“@DataJpaTest”是这里的最佳选择,还是我应该尝试不同的注解?

问:我必须创建一个单独的“Bean”类吗?如果有,你能举个例子吗?

================================================ ==================

感谢您的所有回复。包括 Simon Martinelli(现已删除)的回复。

解决方案:

    我原来的application-test.properties 原样正常。

    我把它放错了位置:所有任何配置文件的application.properties文件通常应该放在同一个项目文件夹中:src/main/resources

    src/main/resources/application-test.properties

    @Transactional 在这里不相关 - 我删除了它。我保留了它@ActiveProfiles("test")

    根据 Karthik R 的建议,我添加了 @AutoConfigureTestDatabase(replace=Replace.NONE)

    此时,测试成功读取了application-test.properties,并使用了MySQL而不是H2。

    最后的注释:

    @RunWith(SpringRunner.class)
    @ActiveProfiles("test")
    @DataJpaTest
    @AutoConfigureTestDatabase(replace=Replace.NONE)
    public class IssueRepositoryIntegrationTests 
    

我发现这个链接特别有用:Spring Boot – Profile based properties and yaml example

http://www.mkyong.com 上的所有材料都非常好!

【问题讨论】:

【参考方案1】:

来自@DataJpaTestdocumentation:

默认情况下,使用 @DataJpaTest 注释的测试将使用嵌入的 内存数据库(替换任何显式或通常自动配置的 数据源)。

如果你去文档,你可以看到这个注解聚合了很多其他的注解。

@Transactional 注释在测试上下文中的行为方式与在应用程序上下文中的行为方式不同:

来自春天documentation:

使用@Transactional 注释测试方法会导致测试运行 在默认情况下自动回滚的事务中 测试完成后。

我相信我提供了足够的信息来回答你的问题,另外你可以看看以下文章:

Configuring Separate Spring DataSource for TestsTesting with @Configuration Classes and Profiles

【讨论】:

【参考方案2】:

默认情况下,@DataJpaTest 使用内存 H2 数据库进行 repo 测试。如果您需要使用实际的 DB,您可以考虑禁用自动配置或使用@SpringBootTest 启用整个应用程序 web mvc。

禁用自动配置:

@RunWith(SpringRunner.class)
@ActiveProfiles("test")
@Transactional
@DataJpaTest
@AutoConfigureTestDatabase(replace=Replace.NONE)
public class IssueRepositoryIntegrationTests 

@AutoConfigureTestDatabase 为您配置测试 H2 DB。您可以在上面特别提到不要,或者您可以将此自动配置排除为:

@EnableAutoConfiguration(exclude=AutoConfigureTestDatabase.class)

P.S::我自己还没有尝试过上述排除。

有关更多信息,请访问 javadoc: https://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/jdbc/AutoConfigureTestDatabase.html

【讨论】:

我只遇到了解密问题,我使用@DataJpaTest 并且密码是用 jasypt 加密的,它无法使用 jasypt 连接到数据库进行测试 你能描述一下你是如何加密你的密码的吗?我确实使用 jasypt 并且使用 datajptest 它不起作用,忽略加密...... 谢谢,这非常有用。我不明白为什么我的集成测试遵循教程:baeldung.com/spring-testing-separate-data-source 能够使用我通过 PropertySource 注释选择的数据源,但是我通常的带有 DataJpaTest 注释的测试会导致一个不受欢迎的数据库。有了这个 AutoCONfigureTestDatabse,我是一个应用程序,例如 application-postgres.properties。这很好。 @AutoConfigureTestDatabase(replace=Replace.NONE) 为我工作并拯救了我的一天。一整天我都在调试每个角落并搜索大量资源但没有找到启用自动配置数据库的原因。这是一个很好的解决方案。

以上是关于Spring Boot DataJpaTest 单元测试恢复到 H2 而不是 mySql的主要内容,如果未能解决你的问题,请参考以下文章

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

Spring Boot DataJpaTest 单元测试恢复到 H2 而不是 mySql

Spring @DataJpaTest 与 JUnit 5

@DataJpaTest 不会读取 spring.jpa.* 属性,而 @SpringBootTest 会

如果 Spring 安全性在类路径上,则无法使用 @DataJpaTest

带有 h2 的 Spring Boot 单元测试失败的模式