Spring Boot中单元测试数据库的切换策略
Posted bladestone
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot中单元测试数据库的切换策略相关的知识,希望对你有一定的参考价值。
问题缘起
单元测试默认情况下使用嵌入式数据库,例如H2。如果要切换为mysql,直接移除H2驱动,在application.properties(yml)配置相应的连接信息,都不起作用。那该如何切换配置呢?
单元测试数据库
在SpringBoot的单元测试中,默认使用嵌入数据库,例如H2,HSQLDB等.默认情况下无需指定具体的嵌入数据库类型,只需要在pom.xml文件中加入相应的数据库驱动即可,示例如下:
<dependencies> <!-- 略去其余依赖 ---> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>test</scope> </dependency> </dependencies>
这里Scope指定其作用的范围,test表示其可以在测试中生效,即可单元测试程序会使用H2数据库。
使用MySQL作为测试数据库
如果不想使用H2之类的嵌入数据库,而是希望在实际运行和单元测试的数据库保持一致,该如何处理呢?
首先了解一下AutoConfigureTestDataBase.java的代码定义:
** * Annotation that can be applied to a test class to configure * a test database to use * instead of any application defined or auto-configured @link DataSource. */ @Target( ElementType.TYPE, ElementType.METHOD ) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @ImportAutoConfiguration @PropertyMapping("spring.test.database") public @interface AutoConfigureTestDatabase @PropertyMapping(skip = SkipPropertyMapping.ON_DEFAULT_VALUE) Replace replace() default Replace.ANY; EmbeddedDatabaseConnection connection() default EmbeddedDatabaseConnection.NONE; enum Replace ANY, AUTO_CONFIGURED, NONE
从类上的说明信息来看,其主要用来配置测试程序连接的数据库,这个注解的设置将覆盖自动配置的数据库信息。这里需要注意一下replace属性默认为ANY,没有做数据库约束。
connection:默认读取classpath包中扫描到的数据库驱动包信息,其默认使用嵌入式数据库。
定义抽象BaseJPA的基类:
@RunWith(SpringRunner.class) @DataJpaTest @EnableJpaRepositories(basePackages = "org.demo.data.dao") @EntityScan(basePackages="org.demo.data.dao") @TestPropertySource(value= "classpath:application-unittest.properties") @ContextConfiguration(classes= AuditorConfig.class) @Getter @AutoConfigureTestDatabase(replace = AutoConfigureTestDatabase.Replace.NONE) public abstract class BaseJPA @Autowired private TestEntityManager testEntityManager;
这里定义了基于SpringDataJPA的测试基类,封装了常用的配置信息:
- RunWith:指定测试程序运行的环境
- DataJpaTest 用于指定当前测试程序用于测试DAO层,其中默认启用数据操作的相关内容,详细功能可以参照其类定义
- EnableJpaRepositories 指定Repository的目录
- EntityScan 指定SpringDataJPA实体类的目录位置
- TestPropertySource 指定系统自定义的配置文件,用以替代默认的application.properties(yml)
- ContextConfiguration 指定当前应用使用的配置类,设置相应的配置信息
- Getter 为Lombok包的注解,用以生成Getter方法
- AutoConfigureTestDatabase 用于指定测试程序依赖的数据库,这里使用None,不再从pom.xml文件中加载嵌入式数据库驱动。而是直接从系统配置文件application.properties中读取相应的数据库连接信息。
修改POM文件中的依赖
在使用MySQL作为单元测试的数据库,需要调整pom文件中MySQL的驱动程序包依赖:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>
这里需要将其scope表示其只在系统运行之时,会被使用到。移除scope设置之后,将使用默认值,compile。其将作用与表示为当前依赖参与项目的编译、测试和运行阶段,属于强依赖。例如:
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency>
如果不能正确设置scope,在启动过程中,会报出如下错误:
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'dataSource': Invocation of init method failed; nested exception is java.lang.IllegalStateException: Failed to replace DataSource with an embedded database for tests. If you want an embedded database please put a supported one on the classpath or tune the replace attribute of @AutoConfigureTestDatabase. at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1771) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:593) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:515) at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:320) at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:318) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) at org.springframework.beans.factory.config.DependencyDescriptor.resolveCandidate(DependencyDescriptor.java:277) at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1255) at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1175) at org.springframework.beans.factory.support.ConstructorResolver.resolveAutowiredArgument(ConstructorResolver.java:857) at org.springframework.beans.factory.support.ConstructorResolver.createArgumentArray(ConstructorResolver.java:760) ... 52 more Caused by: java.lang.IllegalStateException: Failed to replace DataSource with an embedded database for tests. If you want an embedded database please put a supported one on the classpath or tune the replace attribute of @AutoConfigureTestDatabase. at org.springframework.util.Assert.state(Assert.java:73) at org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration$EmbeddedDataSourceFactory.getEmbeddedDatabase(TestDatabaseAutoConfiguration.java:185) at org.springframework.boot.test.autoconfigure.jdbc.TestDatabaseAutoConfiguration$EmbeddedDataSourceFactoryBean.afterPropertiesSet(TestDatabaseAutoConfiguration.java:151) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1830) at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1767) ... 63 more
从上述错误信息可知,默认情况下,会从classpath路径中搜索使用嵌入式数据库的驱动,这里报错的原因是由于没有在classpath中找到有效的数据库驱动。
测试代码
UserRepository是基于JpaRepository扩展而来的接口,其中基于方法名定义了若干查询,针对其中方法的单元测试:
@Slf4j public class AnnotatedQueryUserTest extends BaseJPA @Autowired private UserRepository userRepository; @Test @Sql(value="classpath:sqls/dao/user_annotated_query.sql") public void testCustomResult() List<UserCount> userCountList = this.userRepository.listGroupedUser(0L); log.info("userCount List:", userCountList.size()); Assert.assertThat(userCountList, hasSize(3)); List<UserEntity> userEntities = this.userRepository.findCustomByNameAndSex("Robin Li", Gender.MALE); log.info("User Entity count:", userEntities.size()); Assert.assertThat(userEntities, hasSize(1));
总结
在本文中重点需要讲述的是如何自定义单元测试中的数据库类型,默认情况是使用嵌入式数据库。例如从嵌入式数据库H2切换到MySQL数据库,其中的关键点是AutoConfigureTestDatabase以及其中的replace属性。本文中使用了SpringDataJPA作为数据库中间层。
以上是关于Spring Boot中单元测试数据库的切换策略的主要内容,如果未能解决你的问题,请参考以下文章