情况说明
在使用Spring Cloud体系时,Spring Boot 项目的启动会伴随着诸多第三方技术的客户侧/生产侧开始工作,
例如Dubbo、Redis、Jetcd、Nacos、Zookeeper等服务,会去主动的完成注册、连接、发送心跳等行为。
这些操作,对于单元测试而言是没有必要的,单元测试的核心原则不考虑这些内容。
通常情况下,即使有需求,对于外部服务的调用也是通过Mock的形式,主动模拟外部接口的调用返回;
但有些情况下,这种模拟本身就变得不正确了。
例如,程序通过AOP、MyBatis插件的形式配合完成的SQL行级数据权限拼接,在这种情况下,SQL的完全体是不能预先获知的(实际上可以,但很麻烦),
因此单元测试必须集成Spring、内存数据库、MyBatis (Plus)技术,完成对已有代码的的最小粒度测试。
在上述案例的情况下,这个测试本身的目的 仍然是为了验证 Mapper/Dao 层的最终SQL是否可以正确执行,因此是符合横向单层测试
的理念的,只是为了达到这个目的需要借助纵向穿透测试
的使用,在我看来,这仍属于单元测试
的范畴。
代码示例
@TestConfiguration
public class MyBatisTestConfig {
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.build();
}
@Bean
public DataSourceInitializer dataSourceInitializer(DataSource dataSource) {
ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
populator.addScript(new ClassPathResource("sql/schemas-h2.sql"));
populator.addScript(new ClassPathResource("sql/data-h2.sql"));
DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();
dataSourceInitializer.setDataSource(dataSource);
dataSourceInitializer.setDatabasePopulator(populator);
dataSourceInitializer.setEnabled(true);
return dataSourceInitializer;
}
@Bean(name = "h2WebServer", initMethod = "start", destroyMethod = "stop")
public Server server() throws Exception {
return Server.createWebServer("-web", "-webAllowOthers", "-webDaemon", "-webPort", "8082");
}
@Bean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(dataSource);
sessionFactory.setTypeAliasesPackage("xxx.xxx.xxx.xxx.entity");
Configuration configuration = new Configuration();
configuration.setCacheEnabled(false);
configuration.setCallSettersOnNulls(true);
configuration.setMapUnderscoreToCamelCase(true);
configuration.setLogImpl(StdOutImpl.class);
sessionFactory.setConfiguration(configuration);
return sessionFactory.getObject();
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer configurer = new MapperScannerConfigurer();
configurer.setBasePackage("xxx.xxx.xxx.xxx.mapper");
configurer.setAnnotationClass(Mapper.class);
configurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
return configurer;
}
}
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE, classes = {
MyBatisTestConfig.class, IDemoMapper.class, IDemoService.class
})
@EnableAspectJAutoProxy(proxyTargetClass = true)
@ExtendWith(MockitoExtension.class)
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
public class IDemoServiceTest {
@Autowired
private IDemoService iDemoService;
@Test
void test() {
Demo entity = iDemoService.selectById(1);
Assertions.assertNotNull(entity);
Assertions.assertEquals(1L, contact.getId());
}
}
如果需要额外的Bean,在 @SpringBootTest 的classes中不断追加即可。