问题背景
在 SpringBoot 中,如果使用 @SpringBootTest 注解进行单元测试的话,会启动很久
因为这种方式会加载所有的东西,我们希望在测试某个功能的时候,能快一点
解决的思路就是测试某个功能的时候,只加载这个功能需要的 Bean
最终实现的效果是这样的
@Slf4j
class XXXTest {
private static TestService service;
@BeforeAll
static void beforeAll() {
// 只加载需要的类,能节省大量的启动时间,加快单元测试
ApplicationContext context = new AnnotationConfigApplicationContext(BeanConfig.class);
service = context.getBean(TestService.class);
}
@Test
void test01() {
Test test = new Test();
test.setName("sss");
test.setCount(1);
service.add(test);
List<Test> testList = service.findList();
// 断言记录只有一条
assertNotNull(testList);
// 断言记录只有一条
assertEquals(testList.size(), 1);
// 断言数量和添加的数量一致
assertEquals(0, test.getCount().compareTo(testList.get(0).getCount()));
}
}
Bean 配置(BeanConfig.java)
@Configuration
@Import({XMLConfig.class})
public class BeanConfig {
// Service 的 Bean 配置
@Bean
public TestService testService() {
return new TestServiceImpl();
}
// Mapper 接口的配置(如果通过 MapperScannerConfigurer 扫描指定的路径(可以配置多个),也可以用这种方式)
@Bean
public MapperFactoryBean<TestMapper> TestMapper(SqlSessionFactory sqlSessionFactory) {
MapperFactoryBean<TestMapper> mapperFactoryBean = new MapperFactoryBean<>();
mapperFactoryBean.setMapperInterface(TestMapper.class);
mapperFactoryBean.setSqlSessionFactory(sqlSessionFactory);
return mapperFactoryBean;
}
// 以此类推,如果有依赖的 Bean,可以在这里使用 @Bean 注解初始化注入容器
}
Mapper XML 配置(XMLConfig.java)
import com.baomidou.mybatisplus.core.config.GlobalConfig;
import com.baomidou.mybatisplus.core.toolkit.GlobalConfigUtils;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import config.base.BaseDataSourceConfig;
import org.apache.ibatis.plugin.Interceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import javax.sql.DataSource;
import java.io.IOException;
@Configuration
@Import({BaseDataSourceConfig.class})
public class XMLConfig {
/**
* 这里的 DataSource 会使用构造器注入的方式注入进来
* 因为我们使用了 @Import 导入了 BaseDataSourceConfig.class
* 所以 BaseDataSourceConfig 里面的 Bean 也会被注入容器
*
* 注意:这里我使用的是 MyBatis-Plus 的插件,所以初始化的是 MybatisSqlSessionFactoryBean
*/
@Bean
public MybatisSqlSessionFactoryBean sqlSessionFactory(DataSource dataSource) throws IOException {
// 限定符必须是表达式
MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
// 设置 mapper.xml 位置
ResourceLoader resourceLoader = new PathMatchingResourcePatternResolver();
Resource TestMapper = resourceLoader.getResource("classpath:mapper/TestMapper.xml");
// 多个 Mapper 也同样用这种方式加载进来
Resource[] resourceArray = new Resource[]{TestMapper};
sessionFactory.setMapperLocations(resourceArray);
// 设置插件
Interceptor[] interceptors = new Interceptor[]{interceptor()};
sessionFactory.setPlugins(interceptors);
// 设置全局配置(不然无法使用 FillMetaObjectHandler)
GlobalConfig globalConfig = GlobalConfigUtils.defaults();
globalConfig.setMetaObjectHandler(new FillMetaObjectHandler()); // 注册该 Bean,不然单元测试无法设置 insert 字段
sessionFactory.setGlobalConfig(globalConfig);
return sessionFactory;
}
// 因为要加载不同包下面的 Mapper,为了启动快所以单独配置 Bean
// 配置接口扫描的话会扫描所有的 Mapper
/**
* 配置 Mapper 接口扫描
* @return
*/
// @Bean
// public MapperScannerConfigurer mapperScannerConfigurer() {
// MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
// mapperScannerConfigurer.setBasePackage("com.hytechpaas.workorder.modules.wos.mapper.stock");
// return mapperScannerConfigurer;
// }
// 这里的配置是我的项目中需要的配置,你需要根据自己项目的情况来决定是否需要这些配置
/**
* MyBatis 数据权限拦截器
*/
@Bean
public Interceptor interceptor() {
return new com.common.datascope.DataScopeInterceptor();
}
/**
* 数据权限开关配置
*/
@Bean
public DataScopeConfig dataScopeConfig() {
return new DataScopeConfig();
}
}
Mapper 需要数据源,所以初始化数据源(BaseDataSourceConfig.java)
import com.zaxxer.hikari.HikariDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
@Configuration
public class BaseDataSourceConfig {
@Bean
public DataSource dataSource() {
HikariDataSource dataSource = new HikariDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://127.0.0.1:3306/jeecg-boot?characterEncoding=UTF-8");
dataSource.setUsername("root");
dataSource.setPassword("1234");
return dataSource;
}
}
这种方式的优点和缺点
- 优点:减少容器启动时间,加快测试速度
- 缺点:当一个 Service 的依赖比较多的时候,配置 Bean 比较麻烦