一、Mock简介

背景:小明和小刚要为app制作一个登陆功能,小明负责网络交互获取服务器的认证数据,小刚负责将获取到的数据写入到数据库中,然后能够使用户自动登陆。小刚的开发的速度比较快,已经完成了数据库互这一块,但是小明的网络交互还没有开发完成。那么小刚怎么测试自己的数据库是否完成呢?小刚就要自己创建一个类,模拟登陆,然后进行测试。
首先创建User类
public class User {private String id;private String pwd;public String getId() {return id;}public void setId(String id) {this.id = id;}public String getPwd() {return pwd;}public void setPwd(String pwd) {this.pwd = pwd;}@Overridepublic String toString() {return "User [id=" + id + ", pwd=" + pwd + "]";}
}

然后创建数据库存储类UserDao

public class UserDao {public void saveUserInfo(User user){//存储数据System.out.println(user);}
}

之后创建自己的测试登陆类Login (这就是一个简单的Mock)

public interface ILogin {public User login();
}
public  User login(){User user = new User();ser.setId("1");user.setPwd("123");return user;
}

最近创建测试类进行测试SaveTest

public class SaveTest extends TestCase{private UserDao mUserDao;private ILogin mLogin;@Overrideprotected void setUp() throws Exception {// TODO Auto-generated method stubsuper.setUp();mUserDao = new UserDao();mLogin = new LoginImpl();}public void testSave(){mUserDao.saveUserInfo(mLogin.login());}@Overrideprotected void tearDown() throws Exception {// TODO Auto-generated method stubsuper.tearDown();mUserDao = null;mLogin = null;}
}

根据上面的例子我们可以得出:Mock代表的是模拟某个类(Login类)的行为。这个类是测试你的模块必不可少的类。(必须得有Login才能调用saveUser()存储User)。

但是如果测试模块需要多个未完成的外部类,那么岂不是需要些很多个Mock。为了简化这个过程,就出现了Mockito库。

二、Mockito的使用

①Mockito两个重要的概念
Mock:对象用来判断测试是否能通过,也就是用来验证测试中依赖对象间的交互能否达到预期。
Stub:完全是模拟一个外部依赖,用来提供测试时所需要的测试数据。(调用盖房只为获取方法的返回值,且该返回值是由自己设定。形式就是调用了方法,没有调用方法的内部实现,然后返回自己设定的返回值)
②、使用
Mock的作用
1、测试对象的行为次数(从未、至少、至多、具体次数)
//设置为静态import
import static org.mockito.Mockito.*;public class TestMock extends TestCase {public void testList(){//知识点①List mockList = mock(List.class);//知识点③mockList.add("1");//知识点②verify(mockList,times(2)).add("1");}}

①、使用类的Class类创建测试对象,该类可以是接口、或者具体类。因为并非真正的调用该类的真实方法。

②、verify(Object obj,VerficationMode mode):计算该类的方法被调用的次数。
Object obj:表示被监控的类。
VerficationMode mode:表示次数。 一般使用times(int times)来设置。
默认VerficationMode mode 为 times(1);
.xxx():XXX表示类中具体的方法。
③、发现方法的调用必须在verify()方法前。因为verify()方法是计算在其被调用前方法的使用次数。
常用的verify()方法:
其实修改的只是判定方法被调用的次数。
verify(mockList,times(2)).add("1");
        //never():从未被调用过verify(mockList,never()).add("1");//asLeast():至少调用的次数verify(mockList,atLeast(1)).add("1");//atMost():至多调用的次数verify(mockList,atMost(5)).add("1");//times():具体调用的次数verify(mockList,times(3)).add("1");

Stub的使用

1、为对象stub(打桩:就是自己设定返回值)
public class TestMock extends TestCase {public void testList(){//知识点①LinkedList list = mock(LinkedList.class);//知识点②when(list.get(0)).thenReturn(5);//知识点③int data =(int)list.get(0);System.out.println(data);//输出:5.}
}

①、能够直接获取实现类的Mock对象,不光能获取接口类的Mock对象

②、when(T methodCall).thenReturn(T data):表示监控该类的这个方法,当该方法调用的时候,返回thenReturn(T data)中的data数据。 根据方法可知,该类的方法必须要有返回值才能用when(),如果方法没有返回值怎么办,稍后会讲到
③、验证方法的使用是否正确。
常用打桩的方法:
①、thenReturn:返回具体的数值
②、thenThorrow:返回异常。
通过这个方法我们可以改写
2、利用参数匹配器,来设定输入参数的类型。
参数匹配器,表示该方法能够接收某一类参数
参数匹配器的种类:anyXXX(),XXX代表基础引用类型。例:anyInt()、anyString()、等
例:
public class TestMock extends TestCase {public void testList(){//知识点①LinkedList<String> list = mock(LinkedList.class);//知识点②when(list.get(anyInt())).thenReturn("asd");//知识点③String data =(String)list.get(1);String data2 = (String)list.get(2);System.out.println(data+"  "+data2);}
}
还可以这样使用:
verify(mock).someMethod(anyInt(),anyString);
但是不能
verify(mock).someMethod(anyInt(),"asd");//如果使用参数匹配器,那么全部的输入参数都得是参数匹配器,而不能是一个具体的参数。

3、为连续的调用做测试桩

public class TestMock extends TestCase {public void testList(){LinkedList<String> list = mock(LinkedList.class);//连续调用thenXxx()方法when(list.get(anyInt())).thenReturn("asd").thenThrow(new RuntimeException());String data =(String)list.get(1);System.out.println(data);//返回asdString data2 = (String)list.get(2);//报:RuntimeException()的错}
}

4、制作回调测试桩

public void testList(){List<String> list = mock(List.class);//知识点①when(list.get(anyInt())).thenAnswer(new Answer<String>() {@Overridepublic String answer(InvocationOnMock invocation) throws Throwable {// TODO Auto-generated method stub//获取数据的参数Object [] objs = invocation.getArguments();//获取mock对象本身Object mock = invocation.getMock();return "返回的数据"+objs[0];}});String str = list.get(12);System.out.println(str);}

知识点①、通过调用thenAnswer(),然后调用方法后,会触发Answer的回调,然后通过invocation获取该方法的输入参数。然后进行判定。感觉相当于在Answer对象类,写方法的实现。

5、简化Mock的创建过程
public class TestMock extends TestCase {@Mockprivate User mUser;@Mockprivate SaveTest mSaveTest;@Mockprivate ILogin mLogin;@Overrideprotected void setUp() throws Exception {// TODO Auto-generated method stubsuper.setUp();//初始化注解的类,MockitoAnnotations.initMocks(this);//然后就可以了直接使用了,不需要ILogin login = mock(ILogin.class);}@Overrideprotected void tearDown() throws Exception {// TODO Auto-generated method stubsuper.tearDown();}}
6、doReturn()、doThrow()、doAnswer()、doNothing()方法的使用
原理:当执行了某个类的方法,之后就执行doXxx()方法。

使用情形:

1、测试void方法  (因为when中的方法必须得有返回值,才能使用thenXxx()方法)
注: doReturn()方法,测试的必须得是有返回值的方法,否则会报错
2、在受监控的对象上测试(结合"监控一起讲解")
对情形1的举例
<span style="font-size:18px;">//使用情形:测试void方法。
public class TestMock extends TestCase {private User user;public void testMock(){User user = mock(User.class);doThrow(new RuntimeException()).when(user).setId();user.getId();}
}//结果抛出了异常</span>
我们在测试下,调用doReturn(),调用没有返回值的方法(void)
private User user;
public void testMock(){User user = mock(User.class);doReturn("asd").when(user).setId("zxc");
}//报错了,doReturn不能使用void的方
7、监控(spy)真实对象
spy:能够调用真实对象的方法,而不是像打桩(stub)只调用方法,不调用的内部实现。
举例:
    private User user;public void testMock(){User user = new User();User spy = spy(user);spy.getId();//相当于User user = new User();user.getId();}

作用:变为spy之后,就相当变成了一个Mock,也就是能够调用Mock的所有实现(之前说到的verfity判断、做测试桩)

例:
public void testMock(){List list = new LinkedList();List spy = spy(list);//1、设置测试桩   错误无法使用。when(spy.get(0)).thenReturn("asd");//2、利用doXxx()建立测试桩doReturn("asd").when(list).get(0);//3、测试次数verify(list).get(0);}

但是发现直接使用1、设置测试桩的功能,会报错。因为spy.get(0)调用了真实对象list的方法,但是此时list中是没有值的,所以会调用IndexOutOfBoundsException的异常。

那么我们将1删除,测试2,发现2能够好好的返回asd。说明使用doXxx()是不会调用真实对象的方法。这就印证了doXxx()的使用情形2。(spy类中,不推荐使用thenXxx(),推荐使用doXxx())。
三、综合实例
我们继续第一个例子,小刚需要完成1、将登陆的数据装入数据库。2、假设登陆之后,用户需要搜索一篇文章,小刚要先从数据库中提取,如果数据库没有这篇文章的话,再向网络获取
1、首先之前我们已经建立了未使用Mockito工具的测试,现在我们修改一下,使用Mockito来完成该项目。
首先回顾一下之前我们建立的类
User:JavaBean,将传过来的数据转化为对象
UserDao:存储类,用来将数据存入数据库
ILogin:用来登陆的接口
LoginImp:登陆的实现类,将数据解析成User对象
SaveTest:测试类,测试是否数据库存储实现没问题。
那么由于我们拥有的Mockito,就表示我们没必要具体实现Login这个类的login方法,只要确定返回值是User就可以了。
所以可以删除LoginImp,然后修改SaveTest的实现:
import static org.mockito.Mockito.*;public class SaveTest extends TestCase{private UserDao mUserDao;private ILogin mLogin;@Overrideprotected void setUp() throws Exception {// TODO Auto-generated method stubsuper.setUp();//初始化对象mUserDao = new UserDao();mLogin = mock(ILogin .class);
}public void testSave(){//定义返回的UserUser user = new User();user.setId("number");user.setPwd("123");//设置返回值when(mLogin.login()).thenReturn(user);//实现数据库的存储mUserDao.saveUserInfo(mLogin.login());}@Overrideprotected void tearDown() throws Exception {// TODO Auto-generated method stubsuper.tearDown();mUserDao = null;mLogin = null;}
}

2、那么现在我们需要制作提取数据的功能了。

测试的目的:测试获取Article的模块是否能够使用,并且不出bug
分析:
1、在ArticleDao类中,添加提取数据库数据的功能。(自行创建Article类,与User的创建方式相同)
2、自行创建获取Article的网络的接口(与创建ILogin一样)
3、创建ArticleController类:用来处理获取数据的逻辑(判断是否数据库中含有值,如果没有则从网络获取)
4、进行测试。
第一步:
public class ArticleDao {public void saveArticleInfo(Article article){//存储数据System.out.println(article);}public User getArticleFromDb(int id){//为了简便,仿造数据if (id < 100){return null;}else {Article article= new Article();article.setId(id+"");article.setContent("zxcvcv");return article;}}
}

第二步:

/*** 功能* 获取Article的数据:是从网络还是从本地数据库* 1、首先确定是否从本地获取数据* 2、当本地没数据的时候从网络中获取数据*/
public class ArticleController {private ArticleDao mArticleDao;private IGetArticle mGetArticle;public UserController(IGetArticle getArticle){mGetArticle= getArticle;
}public User getArticle(int id){Article article = null;
if(mArticleDao != null){article = mArticleDao.getUserFromDb(id);}//从网络获取数据if (article== null){article = mGetArticle.getArticle();}return article;}public void setUserDao(ArticleDao articleDao){mArticleDao = articleDao;}
}

第三步:

确定Mock类:IGetArticle类
确定测试类:ArticleController类。能否从数据库中获取到值,如果获取不到值,能否从网络中获取到值....
public class GetUserTest extends TestCase{private IGetArticle mGetArticle;private ArticleDao mArticleDao;private ArticleController mController;@Overrideprotected void setUp() throws Exception {// TODO Auto-generated method stubsuper.setUp();//初始化mock类mGetArticle = mock(IGetArticle.class);//初始化测试类mController = new ArticleController(mLogin);mArticleDao = new ArticleDao();}public void testGetArticle(){//第一步:测试当从UserDao中获取到值的情况,那么结果一定不为nullmController.setArticleDao(mArticleDao);when(mGetArticle.getArticle()).thenReturn(null);Article article = mController.getArticle(120);System.out.println(article);//第二步:测试UserDao返回null的时候,直接从网络中赋值Article myArticle = new Article();myArticle.setId("123");myArticle.setPwd("zxc");when(mGetArticle.getArticle()).thenReturn(myArticle);Article newArticle = mController.getArticle(1);System.out.println(newArticle);}}

单元测试——使用Mock相关推荐

  1. React 16 + Jest单元测试 之 Mock Functions(Mock Names 和 Custom Matchers)

    转载 React 16 + Jest单元测试 之 Mock Functions(Mock Names 和 Custom Matchers) 项目初始化[这里使用之前的项目,节省时间] 项目初始化地址 ...

  2. Java单元测试(Junit+Mock+代码覆盖率)

    单元测试是编写测试代码,用来检测特定的.明确的.细颗粒的功能.单元测试并不一定保证程序功能是正确的,更不保证整体业务是准备的. 单元测试不仅仅用来保证当前代码的正确性,更重要的是用来保证代码修复.改进 ...

  3. 单元测试与 Mock 方法

    Mock 方法是单元测试中常见的一种技术,它的主要作用是模拟一些在应用中不容易构造或者比较复杂的对象,从而把测试与测试边界以外的对象隔离开. 编写自定义的 Mock 对象需要额外的编码工作,同时也可能 ...

  4. 使用 @MockBean 和 @SpyBean 解决 SpringBoot 单元测试中 Mock 类装配的问题

    最近在做某个项目的时候一直使用 @MockBean 来解决单元测试中 Mock 类装配到被测试类的问题.这篇文章主要介绍了 @MockBean 的使用例子以及不使用 @MockBean 而使用@Spy ...

  5. 单元测试 | 如何Mock IHttpClientFactory

    前言 编写单元测试时,常常需要使用Mock框架(例如Moq)生成测试类的依赖接口的"模拟"实现,并验证接口是否按预期使用: _mediatorMock = new Mock< ...

  6. python单元测试mock_python3的单元测试模块mock与性能测试模块cProfile

    我们知道写完了代码需要自己跑一跑进行测试,一个写好的程序如果连测试都没有就上到生产环境是不敢想象的,这么做的人不是太自信就是太无知. 传统测试无非就是自己运行一下程序查看结果,或者前后端服务进行联调, ...

  7. python单元测试mock_Python单元测试的Mock是怎么回事

    单元测试 什么是单元测试, 维基百科上是这么定义的: unit testing is a method by which individual units of source code, sets o ...

  8. Golang单元测试、Mock测试以及基准测试

    之前参加字节跳动青训营而整理的笔记 Golang拥有一套单元测试和性能测试系统,仅需要添加很少的代码就可以快速测试一段需求代码. 一.单元测试 单元测试主要包括:输入.测试单元.输出.期望以及与期望的 ...

  9. 一文让你掌握单元测试的Mock、Stub和Fake

    单元测试中有几个神秘的概念,它们就是Mock,模拟对象:Stub,存根:Fake,伪对象,它们听起来很类似,也很容易混淆,让我们通过这篇文章揭开它们神秘的面纱,探索其幽深的小径. 1.什么是伪对象(F ...

  10. 单元测试fake mock stub

    单元测试fake,mock,stub理解 首先这三个词指的是概念,对于单元测试中各个对象的描述,有助于命名规范.在实际操作中,以测试需求为准. fake指为需要测试单元简单模拟了实际运行环境的,确保正 ...

最新文章

  1. ROS知识(4)----初级教程之常见问题汇总
  2. VMware虚拟机安装黑苹果MacOS Mojave系统详细教程
  3. NotePad++编译代码
  4. oracle数据库切换教程,oracle 11gR2 物理备用数据库搭建及切换
  5. 怎么才能学好Java编程写好Java代码?
  6. 2019温馨的元旦祝福语 2019元旦祝福语大全!收藏备用!
  7. java 根据类名示例化类_Java LocalDateTime类| minusMinutes()方法与示例
  8. ActiveRecord 模式杂谈
  9. php随机访问文章,zblog PHP调用热门文章、随机文章和热评文章代码
  10. Java线程安全策略
  11. 完全自定义TabBar(八)
  12. 让django完成翻译,迁移数据库模型
  13. 如虎添翼VSPHERE 4/5 环境下 linux/windows 动态扩展磁盘
  14. 应用随机过程_《常见随机过程》(目录)
  15. MATLAB(1)---将mat文件转换为csv文件
  16. Linux spi驱动框架之执行流程-nuc970-att7022
  17. 花呗的24期利息计算器_花呗利息怎么算 利息计算器算一算花呗分期付款利率
  18. 网站服务器拥挤如何进去,教你一招:有效解决网络拥挤的办法!
  19. java.sql.time 格式_java.sql.Date,java.sql.Time和java.sql.Timestamp什么区别
  20. 万级送风天花工作原理以及操作方法

热门文章

  1. python抽奖简单小程序游戏_用Python做个年会抽奖小程序吧
  2. 微信公众号开发智能硬件MP后台注册篇
  3. 云呐|仓库RFID固定资产盘点软件有哪些优势
  4. word中输入 千分之一 万分之一 符号
  5. Markdown语法(常用语法,快速掌握)
  6. mysql 家谱树查询_族谱树算法
  7. 微信开发者模式php,PHP实现模拟微信公众号开发者模式
  8. 第一章 openEuler 安装指南
  9. 21世纪以来14起最大的数据泄露事件
  10. 浅谈SystemInit时钟系统