Spring Boot:为测试配置 Spring DataSource

Posted

技术标签:

【中文标题】Spring Boot:为测试配置 Spring DataSource【英文标题】:SpringBoot: Configuring Spring DataSource for Tests 【发布时间】:2021-02-16 22:41:58 【问题描述】:

我有一个 SpringBoot 应用程序。

我已经创建了这个测试:

@ContextConfiguration(classes=TestConfig.class)
@RunWith(SpringRunner.class)
@SpringBootTest
public class SuncionServiceITTest 
    @Test
    public void should_Find_2() 
        // TODO
    

在哪里

@Configuration
@EnableJpaRepositories(basePackages = "com.plats.bruts.repository")
@PropertySource("local-configuration.properties")
@EnableTransactionManagement
@SpringBootApplication(exclude = SecurityAutoConfiguration.class)
public class TestConfig 

和本地configuration.properties:

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

但是当我运行测试时。我收到了这个错误:

原因: org.springframework.beans.factory.NoSuchBeanDefinitionException: 否 名为“entityManagerFactory”的 bean 可用

我也试过了:

@EnableJpaRepositories(basePackages = "com.plats.bruts.repository", entityManagerFactoryRef="emf")

然后我有错误:

原因: org.springframework.beans.factory.NoSuchBeanDefinitionException: 否 名为 'emf' 的 bean 可用

【问题讨论】:

【参考方案1】:

看起来您缺少以下启动器依赖项。此启动器依赖项具有配置 jpa 存储库所需的所有必要依赖项。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

【讨论】:

【参考方案2】:

这是一种如何在一个应用程序中配置多个数据源的方法。我已经为spring-webmvcgraphql-java 测试过它,但我认为它对spring-boot 也很有用。


spring-data-jpa配置多个数据源

对于每个数据库,我们应该启用 JPA 存储库并指定相应接口的基本包。当然,对于每个数据库,我们应该指定实体管理器工厂和相应实体的基本包,以及事务管理器和数据源。

为此,我们将在我们的应用程序中包含一个用于数据 JPA 的配置类和三个用于每个数据库的内部类。请参阅Simple GraphQL implementation。

DataJpaConfig.java

package org.drakonoved.graphql;

@Configuration
@PropertySource(value = "classpath:resources/application.properties", encoding = "UTF-8")
public class DataJpaConfig 
    private final String basePackage = "org.drakonoved.graphql";

    @EnableJpaRepositories(
            basePackages = basePackage + ".repository.usersdb",
            entityManagerFactoryRef = "usersdbEntityManagerFactory",
            transactionManagerRef = "usersdbTransactionManager")
    public class UsersDBJpaConfig 
        @Bean("usersdbEntityManagerFactory")
        public LocalContainerEntityManagerFactoryBean usersDBEntityManagerFactoryBean(
                @Value("$datasource.usersdb.script") String script) 
            return createEntityManagerFactoryBean(
                    script, "usersdb", basePackage + ".dto.usersdb");
        

        @Bean("usersdbTransactionManager")
        public PlatformTransactionManager usersDBTransactionManager(
                @Qualifier("usersdbEntityManagerFactory")
                        LocalContainerEntityManagerFactoryBean factoryBean) 
            return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
        
    

    @EnableJpaRepositories(
            basePackages = basePackage + ".repository.rolesdb",
            entityManagerFactoryRef = "rolesdbEntityManagerFactory",
            transactionManagerRef = "rolesdbTransactionManager")
    public class RolesDBJpaConfig 
        @Bean("rolesdbEntityManagerFactory")
        public LocalContainerEntityManagerFactoryBean rolesDBEntityManagerFactoryBean(
                @Value("$datasource.rolesdb.script") String script) 
            return createEntityManagerFactoryBean(
                    script, "rolesdb", basePackage + ".dto.rolesdb");
        

        @Bean("rolesdbTransactionManager")
        public PlatformTransactionManager rolesDBTransactionManager(
                @Qualifier("rolesdbEntityManagerFactory")
                        LocalContainerEntityManagerFactoryBean factoryBean) 
            return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
        
    

    @EnableJpaRepositories(
            basePackages = basePackage + ".repository.usersnrolesdb",
            entityManagerFactoryRef = "usersnrolesdbEntityManagerFactory",
            transactionManagerRef = "usersnrolesdbTransactionManager")
    public class UsersNRolesDBJpaConfig 
        @Bean("usersnrolesdbEntityManagerFactory")
        public LocalContainerEntityManagerFactoryBean usersNRolesDBEntityManagerFactoryBean(
                @Value("$datasource.usersnrolesdb.script") String script) 
            return createEntityManagerFactoryBean(
                    script, "usersnrolesdb", basePackage + ".dto.usersnrolesdb");
        

        @Bean("usersnrolesdbTransactionManager")
        public PlatformTransactionManager usersNRolesDBTransactionManager(
                @Qualifier("usersnrolesdbEntityManagerFactory")
                        LocalContainerEntityManagerFactoryBean factoryBean) 
            return new JpaTransactionManager(factoryBean.getNativeEntityManagerFactory());
        
    

    //////// //////// //////// //////// //////// //////// //////// ////////

    private LocalContainerEntityManagerFactoryBean createEntityManagerFactoryBean(
            String script, String dbname, String packagesToScan) 
        var factoryBean = new LocalContainerEntityManagerFactoryBean();
        factoryBean.setDataSource(new EmbeddedDatabaseBuilder()
                .setType(EmbeddedDatabaseType.H2)
                .setName(dbname)
                .addScript(script)
                .build());
        factoryBean.setPersistenceUnitName(dbname + "EntityManagerFactory");
        factoryBean.setPackagesToScan(packagesToScan);
        factoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());

        var properties = new HashMap<String, Object>();
        properties.put("hibernate.hbm2ddl.auto", "none");
        properties.put("hibernate.dialect", "org.hibernate.dialect.H2Dialect");
        factoryBean.setJpaPropertyMap(properties);

        return factoryBean;
    

【讨论】:

【参考方案3】:

我更喜欢使用以下方法(我不喜欢创建自己的 bean 配置器)。 As @svr 正确地注意到了(请参阅上一个答案)您没有为 beans 自动配置添加启动包。 我通常创建不同的配置文件:用于本地应用程序运行,用于开发,生产,最后一个用于测试。在我的测试配置文件 (application-functests.yml) 中,我配置了我的功能测试所需的所有设置(数据源、休眠、缓存等),即我的一个项目的 application-functests.yml:

spring:
  http:
    encoding:
      charset: UTF-8
      enabled: true
  profiles: functests
  jpa:
    show-sql: true
    properties:
      hibernate:
        format_sql: true
        enable_lazy_load_no_trans: true
        naming:
            physical-strategy: com.goodt.drive.orgstructure.application.utils.SnakePhysicalNamingStrategy
    hibernate:
      ddl-auto: none
    database-platform: org.hibernate.dialect.PostgreSQL9Dialect    
  datasource:
    driver-class-name: org.postgresql.Driver
    url: jdbc:postgresql://localhost:5432/monitor_service_functests
    username: developer
    password: 123
    sql-script-encoding: UTF-8
  liquibase:
    change-log: classpath:db/changelog/changelog.xml

我只指定了运行测试的配置文件,因此我所有的功能测试都使用 functests 配置文件,即:

@SpringBootTest
@ActiveProfiles("functests")
public class TestEventRepository extends FunctionalTestBase 
    
    @Test
    public void testGetAll() 
        Iterable<EventEntity> eventIterable = dbContext.getEventDataSource().findAll();
        Iterator<EventEntity> it = eventIterable.iterator();
        List<EventEntity> actualEvents = new ArrayList<>();
        while (it.hasNext()) 
            actualEvents.add(it.next());   
        
        List<EventCheckData> expectedEvents = new ArrayList<>() 
            add(new EventCheckData(1L, 1L, "body 1", 1L, 1L));
            add(new EventCheckData(2L, 2L, "body 2", 2L, 2L));
            add(new EventCheckData(3L, 3L, "body 3", 3L, 1L));
            add(new EventCheckData(4L, 1L, "body 4", 2L, 1L));
            add(new EventCheckData(5L, 2L, "body 5", 1L, 2L));
        ;
        EventSimpleChecker.check(expectedEvents, actualEvents);
    


在我的代码示例中,我有基础测试类 - FunctionalTestBase,用于与 liquibase 交互(切换它以应用迁移)

【讨论】:

以上是关于Spring Boot:为测试配置 Spring DataSource的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot / JUnit,为多个配置文件运行所有单元测试

在 Spring Boot 测试中为组件扫描配置基础包

在 Spring Boot 中使用 Hibernate 为 DAO 层配置单元测试

使用 Spring Boot 在单元测试中配置集成测试或使用 Spring JDBC?

Spring-boot,使用不同配置文件的 JUnit 测试

为spring boot配置h2