作者:京东物流 杨建民
1. 什么是 Mock
Mock 有模拟、伪造的含意。Mock 测试就是在测试过程中,对于某些不容易结构或者不容易获取的对象,用一个虚构的对象来创立以便测试的测试方法。mock 工具应用领域:
- 实在对象具备不确定的行为,产生不可预测的成果。
- 实在对象很难被创立。
- 实在对象的某些行为很难被触发。
- 实在对象实际上还不存在。
MockIto 和 PowerMock 是泛滥 Mock 框架中的两种,相似的还有:JMock,EasyMock,大多 Java Mock 库如 EasyMock 或 JMock 都是 expect-run-verify(冀望 - 运行 - 验证)形式,而 Mockito 则应用更简略,更直观的办法:在执行后的互动中发问。应用 Mockito,你能够验证任何你想要的。而那些应用 expect-run-verify 形式的库,你经常被迫查看无关的交互。非 expect-run-verify 形式 也意味着,Mockito 无需筹备低廉的后期启动。他们的指标是通明的,让开发人员专一于测试选定的行为。
2. 解决的问题
咱们在写单元测试时,总会遇到相似这些问题:
1. 结构的入参,对于极值、异样边界场景不好复现,相干的逻辑测不到,只能依附测试环境或预发跑,运气不好可能要改好几次代码重启机器验证,费时费力;
2. 依赖他人接口,可能须要他人帮助测试环境数据库插数能力跑通;
3. 依赖的他人的接口还没有开发完,为了不影响提测,如何实现单元测试?
4. 编写的单元测试依赖测试数据库的数据,每次跑都要数据库改数?
5. 对 service 层加了逻辑,跑单元测试本地验证的时候,因为种种原因,本地环境跑不起来,折腾半天跑起来验证完了,下次开发需要又遇到了另一个问题本地环境启动报错???
6. 我就想 dubug 到某一行代码,然而逻辑简单,七拼八凑的参数就是走不到,本人看代码逻辑还要去问他人接口的返回值逻辑??(未完待续……)引入 Mockito 和 PowerMock 使得编写单元测试更轻松,更省时,更省力。
3. 如何解决问题
3.1 应用 mock 的意义
简略说就是无论谁的本地环境,无论判断条件如许 刻薄 ,无论本地数据库的测试数据被谁删了改了,无论他人接口的返回值逻辑多简单,无论本人代码逻辑多简单,都能独立的、可反复执行的、行级别笼罩的 单元测试用例。
3.2 Mockito 和 PowerMock
一句话说 Mockito 和 PowerMock。当所测逻辑里有动态工具类办法或公有办法咱们心愿他返回特定值时(极值边界、异样测试场景),咱们要用到 PowerMock 去补救 Mockito 的有余,除此之外,用 Mockito 去写单测能实现咱们日常工作 95% 的场景。
3.3 应用 Mcokito 和 PowerMock 的最佳实际
3.3.1 引入 pom 文件
3.3.2 Mockito 和 PowerMock 两条通用语法
打桩:
when(XXxService.xxMethod(“ 冀望入参 ”)).thenReturn(“ 冀望出参 ”); 验证:verify(XXxService).xxMethod(“ 冀望入参 ”);
4. 举例说明
4.1 SpringBoot 我的项目下 Mockito 和 PowerMock 最佳实际
- classes: 指定要加载的类
- properties: 指定要设置属性
- @InjectMocks: 须要注入 mock 对象的 Bean
- @MockBean 或 @Mock: 须要 mock 的 Bean
import X;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.MockitoAnnotations;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
/**
* 测试类 A,调用服务 B 和一个动态工具类 X
*/
@RunWith(PowerMockRunner.class)
@SpringBootTest(classes = {A.class})
@PowerMockIgnore({"javax.management.*"})
@PrepareForTest({X.class}) //mock 静态方法
public class ATest {
@InjectMocks
private A a;
@Mock
private B b;
@Before
public void setUp() throws Exception {MockitoAnnotations.initMocks(this);
}
@Test
public void Test() {when(b.someMethodB(any())).thenReturn(someThingB());
a.someMethodA(someThingA1(), someThingA2());
verify(b).someMethodB(any());
}
/**
* 异样边界测试
*/
@Test
public void test_ExceptionTest() throws ParseException {PowerMockito.mockStatic(X.class);
// 模仿异样抛出的场景
when(X.strToDate(anyString(), anyString())).thenThrow(ParseException.class);
when(X.convertLocalDateTime(any())).thenReturn(someThing());
when(b.someMethodB(any())).thenReturn(someThingB());
a.someThingA(someThingA1(), someThingA2());
verify(b).someMethodB(any());
}
优雅的 mock 能够思考 @spy,当然,mockito 还有一些个性能够自行学习如:
5. 遇到的一些问题及解决
- 打桩逻辑判断是通过 equals 办法判断的
- 测试的预期是抛出异样间接在注解上加:@Test(expected=BusException.class)
- 模仿的参数为 null:Mockito.isNull()
- PowerMock mock 动态和公有 final 会有一些格局区别
- PowerMockmock 静态方法时也能够应用 spy 的形式使代码更优雅
- mock 中发现,mock 没有失效,能够尝试降级 Mockito 版本解决,另外与 junit 反射工具类联合应用,成果更佳。
- 波及多层嵌套的应用场景,读者先思考”单元“选取是否正当,多层嵌套场景将 @InjectMocks 和 @Spy(或 @Mock)联结应用即可
结束语:
文章写于早些时候,目前有些较新技术涌入,如:Spock、TestableMock 等,但上述技术仍然实用于大型零碎品质内建,读者可依据本身状况选择性选用。