问题背景
事情起因是这样的,最近在公司里面做一个很大型的微服务(巨服务)的业务,业务复杂的服务难免涉及到给一张表增加几个字段之类的,这个时候就要同时去改 dao 层的实体类和 Mapper,而公司的 Mapper.xml 又是代码平台自动生成的模板,涉及到各种各样的增删改查方法,牵一发而动全身,每次加几个字段都得到处修修改改,头晕目眩。
改完 mapper.xml 之后也不确定有没有漏掉什么逗号分好之类的,找 AI 帮忙找 bug 也不一定百分百正确,还是得单测一次才能踏实放心,但是这个 Spring 项目真的太大了,如果采用 Spring 默认的测试方案,本地完全无法启动,并且还会有各种注册中心的配置中心的服务极度拖慢启动时间,如果云端单测,不仅编译发包部署容器调度十分缓慢,Mapper 无法直接接口调用调试,得配置复杂的单测流水线才能完成,更是折磨。
痛定思痛之后,到处搜集了一些资料,最后简化了这样一个大型复杂 Spring 项目快速对 Mapper 进行测试的方案。
方案实现
这个方案的核心是利用 AnnotationConfigApplicationContext 指定加载特定的 Bean,避免启动整个 Spring 项目启动带来的大量时间空间消耗。这个时候 Spring 的 ICO 容器里面就只有我们需要的特定的 Bean,其他的 Bean 和服务都不会启动(一些 Spring 启动时跟着启动的特殊的中间件除外,比如日志系统),从而节省大量时间和空间。
配置 Configuration
import javax.sql.DataSource;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Lazy;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DriverManagerDataSource;
@Configuration
@MapperScan(basePackageClasses = TestMapper.class)
public class DenoTestMapperConfig {
@Bean(name = "myTestDatasource")
@Qualifier("myTestDatasource")
public DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://xx.xx.xx.xx/table?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8");
dataSource.setUsername("username");
dataSource.setPassword("*********");
return dataSource;
}
@Bean(name = "myTestSqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(@Qualifier("myTestDatasource") @Lazy DataSource dataSource) throws Exception{ // @Lazy 避免循环依赖
String path = " path to TestMapper.xml";
Resource resource = new PathMatchingResourcePatternResolver().getResource(path);
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new Resource[]{resource});
return bean.getObject();
}
}
测试类
import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MapperTest {
private static final Logger logger = LogManager.getLogger(MapperTest.class); // 这个建议加上,提前初始化日志系统加快启动速度
private TestMapper mapper;
@Before
public void doBefore(){
AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(DenoTestMapperConfig.class);
mapper = ac.getBean(TestMapper.class);
}
@Test
public void simpleTest(){
// TEST
}
}
方案效果
一个私有云编译构造部署需要 20 分钟的 Spring 服务,采取本方案,测试只需要 2sec
题外话:
后续会写一篇如何更好地对 Spring 单测的博客