Mockito的入门及使用

  • 什么是Mockito?
  • Mockito API文档使用
  • Mockito具体使用
  • Mockito常用方法
    • 验证行为(verify验证)
    • Stubbing 条件
    • 参数匹配(ArgumentMatcher)
    • 验证准确调用次数(InvocationTimes)
    • 为void方法抛异常
    • 验证调用顺序(InOrder)
    • spy监视真正的对象
    • Captur 参数捕捉
    • 验证mock对象没有产生过交互
    • @Mock注解
    • @Captor注解
    • @Spy注解

什么是Mockito?

作为一个新手,刚开始听到Mockito这个词,我想到了Mojito鸡尾酒,哈哈哈哈哈,不知道你有没有联想到!

开个玩笑,言归正传,到底什么是mock对象?什么是Mockito呢?其实Mockito 是一个强大的用于 Java 开发的模拟测试框架, 通过 Mockito 我们可以创建和配置 Mock 对象, 进而简化有外部依赖的类的测试.

先来看看下面的这个例子

我们如果要对A进行测试的话,就必须对BC测试,而对B测试就必须对他的分支DE进行测试,所以就会很麻烦,如果DE没写完,那么B就会很难测试。要测试的目标类会有很多依赖,这些依赖的类/对象/资源又会有别的依赖,从而形成一个大的依赖树,要在单元测试的环境中完整地构建这样的依赖,是一件很困难的事情。

这时就提出了mock对象,先看看下图

将B和C所依赖的其他类和对象,进行mock - 构建它们的一个假的对象,定义这些假对象上的行为,然后提供给被测试对象使用。这样无论B、C有多少分支依赖对象,说通俗点,就是把他们分支依赖看成一个整体,而我作为一个测试者只关心我要测试的对象A所对应的分支对象就可以了。

Mockito API文档使用

Mockito是一套非常强大的测试框架,被广泛的应用于Java程序的unit test中。相比于EasyMock框架,Mockito使用起来简单,学习成本很低,而且具有非常简洁的API,测试代码的可读性很高。

Mockito的使用,有详细的api文档,具体可以查看:https://javadoc.io/static/org.mockito/mockito-core/3.4.4/org/mockito/Mockito.html, 下面是整理的一些常用的使用方式。

Mockito具体使用

1、添加依赖
在 build gradle (Moudle app)的dependencies里添加依赖

    testImplementation "org.mockito:mockito-core:+"androidTestImplementation "org.mockito:mockito-android:+"

2、创建二个类
Person类

package com.example.testdemo;
public class Person {private final int    id;private final String name;public Person(int id, String name) {this.id = id;this.name = name;}public int getId() {return id;}public String getName() {return name;}
}

PersonDAO 接口

package com.example.testdemo;
public interface PersonDao {Person getPerson(int id);boolean update(Person person);
}

PersonService类

package com.example.testdemo;
public class PersonService {private final PersonDao personDao;public PersonService(PersonDao personDao) {this.personDao = personDao;}public boolean update(int id, String name) {Person person = personDao.getPerson(id);if (person == null) {return false;}Person personUpdate = new Person(person.getId(), name);return personDao.update(personUpdate);}
}

我们在开发过程中有可能PersonDAO 接口还没写好,但是我们想测试一下PersonService类里的update方法,要怎么办呢?等接口写好,我们再测试?不,这样也太蠢了,哈哈哈,这时候就可以通过mock来mock一个实例来做测试。

那么我们可以在对PersonService类进行Go To Test,如果有不懂的可以去看
https://blog.csdn.net/weixin_45552475/article/details/107481416
这里面有如何快速创建一个Test类。这里就不贴图了。

打开PersonServiceTest类,写测试代码,这里先贴出来代码,下面再慢慢讲解下。

package com.example.testdemo;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Mockito;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isA;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;public class PersonServiceTest {private PersonDao     mockDao;private PersonService personService;@Beforepublic void setUp() throws Exception {//模拟PersonDao对象mockDao = Mockito.mock(PersonDao.class);//当调用mockDao.getPerson(1)时返回一个id为1,name为"Person1"的Person对象。Mockito.when(mockDao.getPerson(1)).thenReturn(new Person(1, "Person1"));Mockito.when(mockDao.update(isA(Person.class))).thenReturn(true);personService = new PersonService(mockDao);}@Testpublic void testUpdate() throws Exception {boolean result = personService.update(1, "new name");assertTrue("is true", result);//验证是否执行过一次getPerson(1)Mockito.verify(mockDao, times(1)).getPerson(eq(1));//验证是否执行过一次updateMockito.verify(mockDao, times(1)).update(isA(Person.class));}@Testpublic void testUpdateNotFind() throws Exception {boolean result = personService.update(2, "new name");assertFalse("must true", result);//验证是否执行过一次getPerson(1)Mockito.verify(mockDao, times(1)).getPerson(eq(1));//验证是否执行过一次updateMockito.verify(mockDao, never()).update(isA(Person.class));}
}
  • Step1:在setUp中,首先我们先模拟一个mockDao对象出来,主要通过Mockito.mock(PersonDAO.class);来mock的。

  • Step2:添加Stubbind条件,调用Mockito的when(xxxx).thenReturn(yyyy);方法,意思就是指定当执行了xxxx方法的时候,返回 thenReturn 的yyyy值,相当于是对模拟对象的配置过程,为某些条件给定一个预期的返回值。

    打桩需要注意以下两点:

1、对于 static 和 final 方法, Mockito 无法对其when(…).thenReturn(…) 操作。
2、当我们连续两次为同一个方法使用 stub 的时候,他只会只用最新的一次。

  • 我们这里的Mockito.when(mockDao.getPerson(1)).thenReturn(new Person(1,“Jim”)); 意思是当调用mockDao.getPerson(1)时返回一个id为1,name为"Jim"的Person对象。
    Mockito.when(mockDao.update(isA(Person.class))).thenReturn(true);意思是当调用update方法时,返回true。
  • Step3:new一个PersonService对象。
  • Step4:在testUpdate,

Mockito.verify(mockDao, times(1)).getPerson(eq(1));
验证是否执行过一次getPerson(1)。只要有执行过Mockito都会记录下。
Mockito.verify(mockDao, times(1)).update(isA(Person.class));
验证是否执行过一次update

Mockito常用方法

验证行为(verify验证)

一旦创建,mock会记录所有交互,你可以验证所有你想要验证的东西,即使删掉也会有操作记录在。

@Test
public void testVerify() throws Exception {//mock creationList mockList = Mockito.mock(List.class);mockList.add("one");mockList.add("two");mockList.add("two");mockList.clear();//验证是否调用过一次 mockedList.add("one")方法,若不是(0次或者大于一次),测试将不通过,默认是一次Mockito.verify(mockList).add("one");//验证调用过2次 mockedList.add("two")方法,若不是,测试将不通过Mockito.verify(mockList,Mockito.times(2)).add("two");//验证是否调用过一次 mockedList.clear()方法,若没有(0次或者大于一次),测试将不通过Mockito.verify(mockList).clear();
}

这里主要注意。mock会记录你所有的操作的,即使删除也会记录下来。比如mockList中添加完,然后clear掉,Mockito.verify(mockList).add(“one”);这个的验证也是会通过的,验证的关键方法是verify, verify有两个重载方法:

verify(T mock): 默认是验证调用一次,里面默认调用times(1)。 verify(T mock,VerificationMode mode):mode,调用次数.

Stubbing 条件

@Test
public void testStubbing() throws Exception{//你可以mock具体的类,而不仅仅是接口LinkedList mockedList = Mockito.mock(LinkedList.class);//设置值Mockito.when(mockedList.get(0)).thenReturn("one");Mockito.when(mockedList.get(1)).thenReturn("two");Mockito.when(mockedList.get(2)).thenReturn(new RuntimeException());//print 输出"one"System.out.println(mockedList.get(0));//输出 "java.lang.RuntimeException"System.out.println(mockedList.get(2));//这里会打印 "null" 因为 get(999) 没有设置System.out.println(mockedList.get(999));Mockito.verify(mockedList).get(0);
}

对于stubbing,有以下几点需要注意:

  • 对于有返回值的方法,mock会默认返回null、空集合、默认值。比如,为int/Integer返回0,为boolean/Boolean返回false
  • stubbing可以被覆盖,但是请注意覆盖已有的stubbing有可能不是很好
  • 一旦stubbing,不管调用多少次,方法都会永远返回stubbing的值
  • 当你对同一个方法进行多次stubbing,最后一次stubbing是最重要的

参数匹配(ArgumentMatcher)

@Test
public void testArgumentMatcher() throws Exception {LinkedList mockedList = Mockito.mock(LinkedList.class);//用内置的参数匹配器来stubMockito.when(mockedList.get(Mockito.anyInt())).thenReturn("element");//打印 "element"System.out.println(mockedList.get(999));//你也可以用参数匹配器来验证,此处测试通过Mockito.verify(mockedList).get(Mockito.anyInt());//此处测试将不通过,因为没调用get(33)Mockito.verify(mockedList).get(Mockito.eq(33));
}

注:如果你使用了参数匹配器,那么所有参数都应该使用参数匹配器

verify(mock).someMethod(anyInt(), anyString(), eq(“third argument”));
//上面是正确的,因为eq返回参数匹配器
verify(mock).someMethod(anyInt(), anyString(), “third argument”);
//上面将会抛异常,因为第三个参数不是参数匹配器,一旦使用了参数匹配器来验证,那么所有参数都应该使用参数匹配

验证准确调用次数(InvocationTimes)

/*** 验证准确的调用次数,最多、最少、从未等* @throws Exception*/
@Test
public void testInvocationTimes() throws Exception {LinkedList mockedList = Mockito.mock(LinkedList.class);//using mockmockedList.add("once");mockedList.add("twice");mockedList.add("twice");mockedList.add("three times");mockedList.add("three times");mockedList.add("three times");//下面两个是等价的, 默认使用times(1)Mockito.verify(mockedList).add("once");Mockito.verify(mockedList, Mockito.times(1)).add("once");//验证准确的调用次数Mockito.verify(mockedList, Mockito.times(2)).add("twice");Mockito.verify(mockedList, Mockito.times(3)).add("three times");//从未调用过. never()是times(0)的别名Mockito.verify(mockedList, Mockito.never()).add("never happened");//用atLeast()/atMost()验证Mockito.verify(mockedList, Mockito.atLeastOnce()).add("three times");Mockito.verify(mockedList, Mockito.atLeast(2)).add("three times");//最多Mockito.verify(mockedList, Mockito.atMost(3)).add("three times");
}

为void方法抛异常

@Test
public void testVoidMethodsWithExceptions() throws Exception {LinkedList mockedList = Mockito.mock(LinkedList.class);Mockito.doThrow(new RuntimeException()).when(mockedList).clear();//这边会抛出异常mockedList.clear();
}

验证调用顺序(InOrder)

@Test
public void testVerificationInOrder() throws Exception {List singleMock = Mockito.mock(List.class);//使用单个mock对象singleMock.add("was added first");singleMock.add("was added second");//创建inOrderInOrder inOrder = Mockito.inOrder(singleMock);//验证调用次数,若是调换两句,将会出错,因为singleMock.add("was added first")是先调用的inOrder.verify(singleMock).add("was added first");inOrder.verify(singleMock).add("was added second");// 多个mock对象List firstMock = Mockito.mock(List.class);List secondMock = Mockito.mock(List.class);//using mocksfirstMock.add("was called first");secondMock.add("was called second");//创建多个mock对象的inOrderinOrder = Mockito.inOrder(firstMock, secondMock);//验证firstMock先于secondMock调用inOrder.verify(firstMock).add("was called first");inOrder.verify(secondMock).add("was called second");
}

spy监视真正的对象

spy是创建一个拷贝,如果你保留原始的list,并用它来进行操作,那么spy并不能检测到其交互

@Test
public void testSpy() throws Exception {List list = new LinkedList();List spy = Mockito.spy(list);//可选的,你可以stub某些方法Mockito.when(spy.size()).thenReturn(100);//如果操作原始list,那么spy是不会检测到的。list.add("first");//调用"真正"的方法spy.add("one");spy.add("two");//打印oneSystem.out.println(spy.get(0));//size()方法被stub了,打印100System.out.println(spy.size());//可选,验证spy对象的行为Mockito.verify(spy).add("one");Mockito.verify(spy).add("two");//下面写法有问题,spy.get(10)会抛IndexOutOfBoundsException异常Mockito.when(spy.get(10)).thenReturn("foo");//可用以下方式Mockito.doReturn("foo").when(spy).get(10);
}

Captur 参数捕捉

@Test
public void testCapturingArguments() throws Exception {List mockedList = Mockito.mock(List.class);ArgumentCaptor<String> argument = ArgumentCaptor.forClass(String.class);mockedList.add("John");//进行参数捕捉,这里参数应该是"John"Mockito.verify(mockedList).add(argument.capture());assertEquals("John",argument.getValue());
}

验证mock对象没有产生过交互

@Test
public void testInteractionNeverHappened() {List mockOne = mock(List.class);List mockTwo = mock(List.class);//测试通过verifyZeroInteractions(mockOne, mockTwo);mockOne.add("");//测试不通过,因为mockTwo已经发生过交互了verifyZeroInteractions(mockOne, mockTwo);
}

@Mock注解

使用@Mock注解来定义mock对象有如下的优点:

方便mock对象的创建、减少mock对象创建的重复代码、提高测试代码可读性、变量名字作为mock对象的标示,所以易于排错

我们还是通过第一个例子来修改:

public class MockTest {@Mockprivate PersonDAO mockDao;private PersonService personService;@Beforepublic void setUp() throws Exception {/*** 要想让Annotation起作用,就必须初始化.一般初始化都在@Before里面*/MockitoAnnotations.initMocks(this);Mockito.when(mockDao.getPerson(1)).thenReturn(new Person(1,"Jim"));Mockito.when(mockDao.update(Mockito.isA(Person.class))).thenReturn(true);personService = new PersonService(mockDao);}@Testpublic void testUpdate() throws Exception {boolean result = personService.update(1,"Tom");assertTrue("is true",result);//验证是否执行过一次getPerson(1)Mockito.verify(mockDao,Mockito.times(1)).getPerson(Mockito.eq(1));//验证是否执行过一次updateMockito.verify(mockDao,Mockito.times(1)).update(Mockito.isA(Person.class));}
}

@Captor注解

@Captor是参数捕获器的注解,通过注解的方式可以更便捷的对ArgumentCaptor进行定义。还可以通过ArgumentCaptor对象的forClass(Class clazz)方法来构建ArgumentCaptor对象,然后便可在验证时对方法的参数进行捕获,最后验证捕获的参数值。如果方法有多个参数都要捕获验证,那就需要创建多个ArgumentCaptor对象处理。

public class MockTest {@Captorprivate ArgumentCaptor<String>  captor;@Beforepublic void setUp() throws Exception {/*** 要想让Annotation起作用,就必须初始化.一般初始化都在@Before里面*/MockitoAnnotations.initMocks(this);}@Testpublic void testCaptor() throws Exception {/*** ArgumentCaptor的Apiargument.capture() 捕获方法参数;argument.getValue() 获取方法参数值,如果方法进行了多次调用,它将返回最后一个参数值;argument.getAllValues() 方法进行多次调用后,返回多个参数值;*/list.add("John");//进行参数捕捉,这里参数应该是"John"Mockito.verify(list).add(captor.capture());assertEquals("John",captor.getValue());}
}

@Spy注解

使用@Spy生成的类,所有方法都是真实方法,返回值和真实方法一样的,是使用Mockito.spy()的快捷方式.

public class MockTest {@Spyprivate List list = new LinkedList();@Beforepublic void setUp() throws Exception {/*** 要想让Annotation起作用,就必须初始化.一般初始化都在@Before里面*/MockitoAnnotations.initMocks(this);}@Testpublic void testSpy() throws Exception {//可选的,你可以stub某些方法Mockito.when(list.size()).thenReturn(100);//调用"真正"的方法list.add("one");list.add("two");//打印oneSystem.out.println(list.get(0));//size()方法被stub了,打印100System.out.println(list.size());}
}

参见资源:
http://blog.csdn.net/shensky711/article/details/52771493
https://www.jianshu.com/p/2759c5e88b7e

Mockito的入门及使用相关推荐

  1. Mock 模拟测试简介及 Mockito 使用入门

    Mock 是什么 mock 测试就是在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试的测试方法.这个虚拟的对象就是mock对象.mock对象就是真实对象在调试期间的 ...

  2. mockito的入门与使用

    链接: 关于mockito的初步学习和总结https://blog.csdn.net/qq_36804701/article/details/80475058 第一步导入, 因为是看汪文君老师的视频学 ...

  3. 如何处理好与时间的关系?

    点击↑上方↑蓝色"编了个程"关注我~ 每周至少一篇原创文章 这是本公众号的第 27 篇原创文章 荒腔走板 这是我最近从朋友手里购买过来的kindle.之前上大学的时候也买过一个ki ...

  4. mockito入门_Mockito入门

    mockito入门 本文是我们名为" 用Mockito测试 "的学院课程的一部分. 在本课程中,您将深入了解Mockito的魔力. 您将了解有关"模拟",&qu ...

  5. mockito java_使用Mockito在Java中进行模拟入门

    mockito java 我们都编写了单元测试,但是有时我们面临的挑战是被测单元可能依赖于其他组件. 并且配置其他组件进行单元测试绝对是一个过大的选择. 相反,我们可以使用Mocks代替其他组件,并继 ...

  6. 使用Mockito在Java中进行模拟入门

    我们都编写了单元测试,但是有时我们面临的挑战是被测单元可能依赖于其他组件. 并且配置其他组件进行单元测试绝对是一个过大的选择. 相反,我们可以使用Mocks代替其他组件,并继续进行单元测试. 为了说明 ...

  7. Java Mocking入门—使用Mockito

    我们都会编写单元测试程序,但我们当前所面临的挑战是被测单元可能依赖于其他组件.而对于单元测试配置其他组件,绝对是过枉矫正.相反,我们可以使用Mocks框架代替其他组件,继续进行单元测试. 为了展现如何 ...

  8. SpringBoot - 单元测试利器Mockito入门

    文章目录 Mock 测试 What's Mockito 使用 Mockito pom依赖 Demo Code [常规操作] [Mockito] thenReturn thenThrow verify ...

  9. 单元测试(三) mockito入门

    目录 一.什么是mock测试 二.什么是Mockito 三.快速开始 quickstart 四.3种不同的mock方式 五. Stubbing 六.spying 七.Mockito Argument ...

最新文章

  1. 我的世界php安装,我的世界Linux搭建网页后台Multicraft下载与安装
  2. OpenGL:画三角形程序详解笔记
  3. 选数游戏(ybtoj-二叉堆)
  4. 《Java8实战》笔记(12):新的日期和时间API
  5. ubuntu wifi固定ip_自制wifi遥控小车!ESP8266实践指南(二)
  6. 【读书笔记】Java NIO (中文版) 读书笔记
  7. AutoCAD2020线型比例修改
  8. tableview 修改单元格内容字体大小_如何修改一次代码就可以完成多种类型 cell 的 UITableView 增删修改...
  9. (MATLAB)大家来找茬-简易的彩色图像找不同
  10. Spring复习——B站
  11. 【牛客网专项练习题】
  12. [200902] 条件概率与独立事件
  13. 如何在GitHub上传并更新项目
  14. 计算机软件和应用审稿可以加急吗,计算机类期刊审稿周期
  15. SUST 2019暑期集训题解(差分约束+生成树+传递闭包)
  16. 【缅怀】缅怀汶川地震记
  17. 算法--01背包问题(动态规划算法) 21-01-30
  18. 转载: HDMI 基础知识
  19. 容斥原理与欧拉函数与莫比乌斯函数,狄利克雷卷积与莫比乌斯变换,反演
  20. 使用AIGC工具提升论文阅读效率

热门文章

  1. 知意配音的主播声音好用吗?像真人吗?
  2. matlab调用cst计算扫频,请教一下用matlab读取CST MWS 2011仿真数据的问题
  3. 史上最好用的安卓应用市场导航
  4. 【题目】了解skimage库
  5. eclipse运行,提示错误:The selection cannot be launched,and there are no recent launch
  6. 富文本的使用 NSMutableAttributedString
  7. c#读蓝牙数据_c#蓝牙通信接收数据
  8. html获取ul的li,如何从html中提取一些li(在ol / ul标签下)标签/li
  9. 熊猫烧香被恶搞 网友爆笑诗词句大集合(转)
  10. window10 微软小娜搜索不到内容