mockito介绍

mock作用

在单元测试中,对于一个类中的方法,常常需要依赖其他类的方法、操作数据dto等对象实例。

方法mock:依赖的方法又可能依赖其他方法,呈现级联的树状结构。

问题:在一些情况下,这个依赖树会很深,其中依赖的一个子方法一旦修改出现问题,如果引起大量的单测不可用,对于问题的定位而言十分困难,而且也失去了单元测试的最小单元化、只测试当前逻辑功能的意义。因此在单元测试中,对依赖的子方法逻辑功能的隔离是十分重要的;mock解决方案:通过对操作对象dto进行mock、调用子方法时进行代理跳过具体的执行逻辑直接对执行结果进行模拟,实现对子方法和当前参数处理的模拟运行。 操作数据mock:执行逻辑对于不同case的处理数据,可能有不同的分支逻辑进行处理;

问题:如果不能覆盖所有分支,对于上线后千奇百怪的实际用户操作产生的数据,存在踩坑、不可用的风险。mock解决方案:理由mock构造不同的数据,作为入参、方法调用结果进行各分支的测试。

因此在单元测试中mock技术是其核心功能。

mockito竞品

EasyMock(https://easymock.org/)、 JMock(http://jmock.org/)、 PowerMock(https://github.com/powermock/powermock)

mockito使用

引入依赖

引入mockito-core依赖

org.mockito

mockito-core

2.7.19

test

mockito只关注mock,还需要依赖junit进行单测。

junit

junit

4.12

test

在单测类上注入使用mockito

以下是一个springboot的mock例子,使用@MockBean进行bean的mock和注入,在处理逻辑中利用Mockito.when(xxx).thenReturn(xxx)对调用方法进行mock,实现内部依赖方法调用的mock解耦,实现单元测试只关注当前待测试方法的目标。

@RunWith(SpringRunner.class)

@SpringBootTest

publicclass UserServiceTest {

@Autowired

private UserService userService;

/* mock生成代理 */

@MockBean

private UserDao userDao;

@Test

public void getUserById() throws Exception {

// 定义当调用mock userDao的getUserById()方法,并且参数为3时,就返回id为200、name为I'm mock3的user对象

Mockito.when(userDao.getUserById(3)).thenReturn(new User(200, "I'm mock 3"));

// 返回的会是名字为I'm mock 3的user对象

User user = userService.getUserById(1);

Assert.assertNotNull(user);

Assert.assertEquals(user.getId(), new Integer(200));

Assert.assertEquals(user.getName(), "I'm mock 3");

}

}

使用doAnswer实现mock调用结果

public class MockitoAnswerTest {

@InjectMocks

private TestClass testClass;

@Mock

private CalledClass calledClasee;

@Test

public void analysisTest() {

MockitoAnnotations.openMocks(this);

Mockito.doAnswer(new Answer() {

@Override

public String answer(InvocationOnMock invocation) throws Throwable {

return "mocked answer";

}

}).when(calledClasee).call(Mockito.any());

String result =

testClass.test(new Object());

Assert.assertEquals("mocked answer", result);

}

Mockito 的限制

上述就是 Mockito 的 mock 对象使用方法,不过当使用 Mockito 在 mock 对象时,有一些限制需要遵守 1. 不能 mock 静态方法 2. 不能 mock private 方法 3. 不能 mock final class

mockito实现-源码分析

加载初始化mockito

在单元测试的启动阶段开启mockito注解,传递当前测试实例。

@Before

public void setUp() {

MockitoAnnotations.openMocks(this);

}

openMock源码

openMocks方法会对当前单元测试类中的各字段field,通过

/**

* Initializes objects annotated with Mockito annotations for given testClass:

* @{@link org.mockito.Mock}, @{@link Spy}, @{@link Captor}, @{@link InjectMocks}

*

* See examples in javadoc for {@link MockitoAnnotations} class.

*

* @return A closable to close when completing any tests in {@code testClass}.

*/

public static AutoCloseable openMocks(Object testClass) {

if (testClass == null) {

throw new MockitoException(

"testClass cannot be null. For info how to use @Mock annotations see examples in javadoc for MockitoAnnotations class");

}

AnnotationEngine annotationEngine =

new GlobalConfiguration().tryGetPluginAnnotationEngine();

return annotationEngine.process(testClass.getClass(), testClass);

}

AnnotationEngine有3个实现类,IndependentAnnotationEngine(处理)、SpyAnnotationEngine(处理@Spy注解)、InjectingAnnotationEngine(组装代理IndependentAnnotationEngine和SpyAnnotationEngine)

ScopedMock用来管理mock对象的生命周期,mock对象在threadLocal中维护,当单测退出时,清空当前线程(单测一般是单线程的)threadLocal中的mock出来的对象进行回收处理。具体逻辑查看org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker类。

@Mock标注字段的对象代理

@Mock标注的单元测试对象中filed对象的创建是由上面的openMock中对@Mock注解处理时createMockFor(annotation,field)触发的。

实现逻辑如下。

org.mockito.internal.creation.bytebuddy.InlineByteBuddyMockMaker中newInstance()创建对象实例方法如下。

调用构造函数实例化mock对象时,参数列表如果是基础类型则传0(false)、对象类型传null

最终用constructor.newInstance(arguments)反射等方式将对象创建出来。

参考资料

万字长文:一文详解单元测试干了什么 https://mp.weixin.qq.com/s/9_TQbVSl1CQLQzuUrsrHLQJava单元测试Mock框架Mockito入门介绍 https://cloud.tencent.com/developer/article/1850562Mock工具之Mockito实战 https://zhuanlan.zhihu.com/p/580113391

文章来源

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: