单元测试简介
什么是单元测试
单元测试是测试应用程序的功能是否能够按需要正常运行。
注:通俗一点将就是用来验证某段代码的行为是否与我们期望的一致。
单元测试的目的
保证产品质量。
注:通俗一点讲就是减少bug,提高代码质量。
单元测试要写多细
单元测试不是越多越好,而是越有效越好!
哪些代码需要有单元测试覆盖
- 逻辑复杂的
- 容易出错的
- 不易理解的,即使是自己过段时间也会遗忘的,看不懂自己的代码,单元测试代码有助于理解代码的功能和需求。
- 公共代码。比如自定义的拦截器;工具类等。
- 核心业务代码。一个产品里最核心最有业务价值的代码应该要有较高的单元测试覆盖率。
何时写单元测试
边写业务代码,边写单元测试。因为只有对需求有一定的理解后才能知道什么是代码是正确的。
一个实际的开发测试场景
- 场景:
假设订单服务OrderFacade中有一个下单方法submitOrder(dubbo服务提供者)
- 业务逻辑
1、调用本地的用户验证类来进行下单前的验证。
2、调用多个依赖的dubbo服务来进行业务处理。
- 怎样测试??
1、该订单服务(dubbo服务)该怎么进行单元测试?
2、当用户验证结果不是我想要的结果时,该怎样进行单元测试?
3、当依赖的dubbo服务未开发完成,或者依赖的dubbo服务的结果不是我想要的结果时,该怎样进行单元测试?
- 思路
模拟验证结果、模拟dubbo服务(各种模拟)。也就是所谓的Mock。
- 结论
上述几个测试场景均可在案例六中找到对应的实现代码。
JMockit简介
JMockit是什么
JMockit是一款Java类/接口/对象的Mock工具,目前广泛应用于Java应用程序的单元测试中。
JMockit中文网:http://jmockit.cn/
JMockit的特点
JMockit的API易用,丰富!
JMockit提供了注解,并支持对类/对象的属性,方法,构造函数,初始代码块(含静态初始代码块)进行灵活Mock。
注:支持static,private,public,final,native方法的Mock。JMockit的本质是对java字节码的修改。通俗一点的讲就是在类的某个方法中加入某段逻辑达到Mock的目的。
JMockit配置
<!-- 先声明jmockit的依赖 -->
<dependency>
<groupId>org.jmockit</groupId>
<artifactId>jmockit</artifactId>
<version>1.36</version>
<scope>test</scope>
</dependency>
<!-- 再声明junit的依赖 -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
注:如果你是通过mvn test来运行你的测试程序 , 请确保JMockit的依赖定义出现在JUnit的依赖之前。
JMockit提供JUnit4,JUnit5,TestNG的支持。
集成的目的是为了让测试程序在运行测试方法前,完成Mock 注解API(@Mocked,@Injectable,@Capturing)修饰的测试属性&测试参数的类做相关字节码的织入。
JMockit的程序结构
测试属性:即测试类的一个属性。它作用于测试类的所有测试方法。
测试参数:即测试方法的参数。它仅作用于当前测试方法。
Record-Replay-Verification 是JMockit测试程序的主要结构。
Record: 录制阶段:即录制某类/对象的某个方法调用,在当输入什么时,返回什么。
Replay: 重放阶段:即重放测试逻辑。
Verification: 验证阶段:重放后的验证。比如验证某个方法有没有被调用,调用多少次。
注:在实际测试程序中,我们更倾向于通过JUnit/TestNG/SpringTest的Assert类对测试结果进行验证。因为对类的某个方法有没调用,调用多少次的测试场景并不多。
JMockit注解
案例一:Mock类
-
方式一:
用Expectations来mock类
demo见 ClassMockingByExpectationsTest -
方式二:
用MockUp & @Mock来mock类
demo见 ClassMockingByMockUpTest注:上面两种方式也是JMockit中支持的两种录制方式。
ClassMockingByExpectationsTest
ClassMockingByMockUpTest
本案例中,针对static/private/public/final/native方法
案例二:Mock实例
用Expectations来mock实例(注意:MockUp & @Mock不支持Mock实例)
InstanceMockingByExpectationsTest
注意:MockUp & @Mock不支持Mock实例
案例三:Mock接口
-
方式一:
用Expectations来mock接口
demo见 InterfaceMockingByExpectationsTest -
方式二:
用MockUp & @Mock来mock接口
demo见 InterfaceMockingByMockUpTest
注:在Mock接口时,使用@Test和@Injectable注解API,比使用MockUp更方便。因为其可以充分利用JMockit的依赖注入功能。
案例四:Mock Spring Bean
-
方式一:
用Expectations来mock
demo见 SpringBeanMockingByExpectationsTest -
方式二:
用MockUp & @Mock来mock
demo见 SpringBeanMockingByMockUpTest
方式二对接口进行mock时,需要采用MockUp泛型的方式来实现。
方式二对bean进行mock时,需要mock接口实现类,直接对接口进行mock会不起作用。
另外,对于Spring Boot程序的Mock,与Mock Spring Bean的方式一致。
demo见 unit-test-demo-springboot应用
案例五:Mock MQ消息生产者
用MockUp & @Mock来mock
案例六:Mock Dubbo消费Bean
方案
1、在spring初始化前,对所有Dubbo消费Bean的进行Mock,即标签里的interface都返回本地默认实现。
2、如果想对某几个Dubbo消费Bean进行Mock,则自定义Dubbo消费Bean的实现即可。
demo见DubboConsumerBeanMockingTest
注:该demo为一个综合的demo,其中包含有对dubbo消费bean、本地Service、MQ生产者的Mock,以及定制返回结果的Mock。