Java DT 指导说明
[toc]
背景
在公司的CleanCode的大背景下,践行开发者自测试(TDD)也是CleanCode文化比较重要的内容之一。
在现实的软件开发过程中,我们经常需要协同其他同事一起来完成某个模块的功能开发,或者需要第三方资源,比如您需要一个短信网关,您需要一个数据库,您需要一个消息中间件,当您进
行测试依赖于这些资源的代码时候,不可能在每次都具备相应的环境,这将是一个很不现实的问题,如果当您依赖于其他模块而无法进行单元测试的时候,此时该模块的质量风险就有两个,第一是
您所负责的代码,第二是您所依赖的代码,您所依赖您没有办法在很快的时间协调到资源,那么您所负责的代码由于不具备单元测试环境没有办法进行测试,很可能存在极大的风险,因此如何测试
您的代码,让他的质量达到百分之百的可用,这就是 Mock 存在的必要 。
**您使用集成测试做单元测试么?**
很多人所谓的测试恐怕更多的是一种
集成测试
,也就是点击页面某个按钮看看是否能够顺利执行,所依赖的各种资源都必须正常运行
,如果出现问题就很难让整个流程执行下去,
我们如何在没有数据库的时候能够测试我们的 Service,这才是单元测试 Mock 要解决的问题
一、Java Mock框架
查询了当前阶段可查询到的Mock框架并且进行了一个简单的对比:
- EasyMock
- jMock
- Mockito
- Unitils Mock
- PowerMock(EasyMock Api)
- PowerMock(Mockito Api)
- JMockit
其中PowerMock可以基于EasyMock,也可以基于Mockito,PowerMock是两者的增强版,所以EasyMock和Mockito可以不考虑;
之所以提到PowerMock而不是Mock,是因为自己服务器端的配置数据获取的方法是静态方法,如果使用mock方式来模拟数据,只有PowerMock支持Mock静态方法,Mock不支持。
二、测试框架集成
2.1、引入PowerMock
2.2、 DT配置文件
放在config目录下,大部分内容可与非test非保持一致
2.3 、启动类
基本与主启动类保持一致,但是配置文件需要执行DT的测试文件
@SpringBootApplication
@TestPropertySource(properties={"spring.config.location=classpath:application.yml"})
@ComponentScan(
basePackages = {
"com.huawei.riemann.cloud.sdk.conf",
"com.huawei.riemann.cloud.notice"
},
includeFilters = {
@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
value = {EurekaSSLConfig.class, FeignSSLBalanceConfig.class, TomcatSSLContainer.class})
},
excludeFilters = {
@ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE,
value = {FeignSSLDefaultConfig.class, KafkaConfig.class})
})
public class TestApplication {
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
}
2.4 、主要配置类
/**
* 功能描述: 测试类
* @since 2021-11-12
*/
@WebAppConfiguration
@SpringBootTest(classes = TestApplication.class)
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringRunner.class)
@PowerMockIgnore({
"javax.management.*", "javax.net.ssl.*", "javax.crypto.*", "javax.xml.*", "org.xml.*", "org.w3c.dom.*",
"org.apache.*"
})
@TestPropertySource(properties = {"spring.config.location=classpath:application.yml"})
public class StarAtlasNoticeBaseTest {
@Autowired
NoticeServiceImpl noticeService;
@Test
public void testDemo() {
System.out.println(1);
}
}
- SpringBootTest:表明这是一个springboot测试类,class指定的是springboot主启动程序类
- RunWith:使用powermock自己的Runner,每一个 Runner 都有各自的特殊功能,你要根据需要选择不同的 Runner 来运行你的测试代码。JUnit 中有一个默认 Runner ,即 BlockJUnit4ClassRunner
- PowerMockRunnerDelegate:将powermock整合到spring容器中。集成PowerMock的时候因为Junit的Runner只能设置一个,所以不知道该设置
- PowerMockRunner还是SpringRunner。如果设置了PowerMockRunner,虽然可以使用mock和spy功能,但是无法使用Spring提供的功能。
- PowerMockRunnerDelegate解决了此问题,既可以使用PowerMock的强大的mock和Spy的功能,也可以使用Spring提供的功能
- PowerMockIgnore: 避免两个类的ClassLoader不同,导致JVM认为这两个类没有关联。PowerMock的工作原理即是使用自定义的类加载器来加载被修改过的类,从而达到打桩的目的。(这里需要是因为不加的话,加解密的时候是会报错的)
- TestPropertySource: 解决SpringBoot默认加载的顺序第一优先级会加载file:./config/目录下的文件,导致test目录下的application.yml无法被加载的问题,指定加载的yaml文件
2.5、运行测试
Run -> with Coverage
三、Mock方法的常见场景
3.1、Mock返回值为void的方法
PowerMockito.doNothing.when(userService, "method", args);
PowerMockito.doNothing.when(userService).method(args);
3.2 、Mock调用真实方法
UserService userService = PowerMockito.mock(UserService .class);
Whitebox.setInternalState(userService , "field", fildValue); 设置变量值
PowerMockito.when(userService.method(args)).thenCallRealMethod();
3.3、 Mock静态函数返回值
先要PrepareForTest静态类
@RunWith(PowerMockRunner.class)
@PrepareForTest({LogManager.class, LoadMessageProfile.class, TrunkLinkDesignUtils.class, SpringBeanUtil.class})
先后用mockStatic申明要mock的静态类,定义返回值,返回也可以是mock对象
PowerMockito.mockStatic(SpringBeanUtil.class);
DesignifccfgisisMapper overNeInfoMapper = PowerMockito.mock(DesignifccfgisisMapper.class);
PowerMockito.when(SpringBeanUtil.getBean(DesignifccfgisisMapper.class)).thenReturn(overNeInfoMapper);
3.4 、写私有函数的DT时,可用mock对象invoke反射形式进行调用
// 1. 获取class
Class<?> clazz = obj.getClass();
//2. 创建一个方法的反射对象
Method method = clazz.getDeclaredMethod(methodName, parameterTypes);
//3. 设置对私有方法的访问权限
method.setAccessible(true);
//4. 执行方法
method.invoke(obj, args);
四、DT测试代码覆盖率
4.1、使用步骤
1、在IDEA中安装Code Coverage for Java插件
2、执行测试用例时选择Run Tests xxxx with Coverage,可以选择执行单个测试用例,也可以执行某一个package下的所有测试用例
3、执行完成后,在弹出框中选择“Add to active suites”,就可以把多次执行的结果汇总统计
4、在弹出的Coverage信息框中,可以查看已执行用例的平均覆盖率,某个package下所有类的覆盖率,以及某个类的覆盖率信息,也可以导出覆盖率报告
4.2、常见问题
最多的问题就是无法出现统计信息,当我们无法查看统计信息的时候,最可能的原因:
- 测试代码的包路径,没有和被测试代码的包路径保持一致。
- 是的,如果统计信息无法出现,可以看一下Test的配置,是否为 moudle根package。
如果原有工程报错:
Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test (default-test) on project StarAtlasMockDemo: Execution default-test of goal org.apache.maven.plugins:maven-surefire-plugin:2.12.4:test failed: The forked VM terminated without saying properly goodbye. VM crash or System.exit called ?
按照下面操作设置之后,可以正常运行通过: