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-webmvc
和graphql-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 中使用 Hibernate 为 DAO 层配置单元测试
使用 Spring Boot 在单元测试中配置集成测试或使用 Spring JDBC?