您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

mockito从入门到通关

时间:2020-08-17 10:25:57  来源:  作者:



为什么要mock?

有很多朋友不愿意写单元测试,觉得写单测试比较花时间,甚至不会写单元测试,很大程度上是因为不想写或者不会写mock。

mock对于单元测试来很重要。单元测试之所以名字里面有“单元”,就是因为一个测试用例只测很小的一个单元代码。但我们的代码总会有依赖,要测试的方法内部可能调用了其它类的方法,这在代码中是很常见的逻辑。所以我们经常会需要mock,用来消除依赖。

所谓mock,翻译过来就是“模拟”,就是模拟你要测试的代码里面「依赖的其它对象」,模拟它的输入和输出,这样就不用管其它逻辑是如何实现的,我们「假定它是符合我们望的就行了」

可能你会有一个疑问:那我们在单元测试里面假定它符合期望了,但实际运行时它有bug了,并不是符合我们期望的,怎么办呢?

首先,我们对那个类也会做单元测试,我们用它的单元测试保证了它的逻辑是正确的。

然后,一个完整的测试体系,不应该只有单元测试。单元测试之上,还应该有集成测试、API测试等,从更高的层面来保证整个应用程序能够如预期工作。

Mockito的基本用法

Mockito是JAVA语言非常主流的一个框架,自己使用起来感觉也比较好用。所以这篇文章想汇总介绍一下Mockito的各种用法,这样大家在以后写单元测试和看单元测试的时候,就能够比较清晰为什么要这么写。

设置Mockito环境

要使用Mockito,首先得在测试类里面设置好Mockito环境。这是为了能够让单元测试框架(本文主要介绍JUnit)能够识别和使用Mockito。

在JUnit 4, JUnit使用了@RunWith注解来声明一个“运行器”。这个运行期的作用是为单元测试提供「mock的初始化工作」(比如使用@Mock、@Spy等注解时,需要初始化),以及「验证mock语法」的功能。

比如我们可能会经常用到的:

@RunWith(JUnit4.class)
@RunWith(SpringRunner.class)
@RunWith(SpringJUnit4ClassRunner.class)

Mockito也有相应的启动器,在@RunWith注解上面使用这个启动器就可以使用Mockito的环境了:

@RunWith(MockitoJUnitRunner)

在JUnit 5,使用了@ExtendWith注解来代替@RunWith注解,Mockito也支持JUnit 5,提供了MockitoExtension类。

除了使用注解以外,也可以使用静态方法initMocks来实现这个功能:

MockitoAnnotations.initMocks(this) 

mock

mock,即mock一个对象。也是注解和代码两种方式可以实现。

@Mock
private User user;

Order order = Mockito.mock(Order.class);

mock对象后,就可以对它使用given等方法模拟它的输入和输出。

given(order.getId()).willReturn(1L);
assertEquals(order.getId(), 1L);

spy

mock出来的对象是完全虚拟的,不会真正地调用本来的实现。如果不对它使用given等方法,会返回默认值(null, 0, false等)。而spy如果不使用given等方法,会调用这个对象本来的实现,返回实际运行后的值。

不是很推荐使用spy,因为它没有消除依赖

spy同样有注解和静态方法的方式:

@Spy
private user user;

Order order = Mockito.spy(Order.class);

captor

captor翻译过来是“捕获”的意思,主要用来捕捉程序运行时调用mock或者spy的对象的方法时,传入的参数。它支持泛型,即要捕获的参数的类型。

@Captor 
ArgumentCaptor<User> userCaptor;

ArgumentCaptor<String> arg = ArgumentCaptor.forClass(String.class);

captor一般是与given或者verify等方法配合使用。

@Mock
List mockedList;
 
@Captor 
ArgumentCaptor argCaptor;

@Test
public void whenUseCaptorAnnotation_thenTheSam() {
    mockedList.add("one");
    Mockito.verify(mockedList).add(argCaptor.capture());
 
    assertEquals("one", argCaptor.getValue());
}

InjectMocks

使用@InjectMocks注解,可以将mock或spy的对象自动注入要测试的对象。这在Spring等使用自动注入的框架里用得非常广泛。

@Mock
Map<String, String> wordMap;
 
@InjectMocks
MyDictionary dic = new MyDictionary();

// 类定义:
class MyDictionary {
    Map<String, String> wordMap;
 
    public MyDictionary() {
        wordMap = new HashMap<String, String>();
    }
    public void add(final String word, final String meaning) {
        wordMap.put(word, meaning);
    }
    public String getMeaning(final String word) {
        return wordMap.get(word);
    }
}

打桩

所谓打桩,其实就是mock的一个过程。我们给定期望的输入和输入,mock的对象就能够如我们期望的那样工作。

打桩有两种写法,一种是传统的写法,一种是BDD **Behavior-Driven Development (行为驱动开发)**的写法。

传统写法

我们先看看传统的写法,传统的写法主要用Mockito类的方法。基本是when-then-invoke-verify形式。

when(phoneBookRepository.contains(momContactName))
  .thenReturn(false);
 
phoneBookService.register(momContactName, momPhoneNumber);
 
verify(phoneBookRepository)
  .insert(momContactName, momPhoneNumber);

BDD写法

BDD写法主要是用BDDMockito类的方法,看起来是given–will-invoke-then的形式。

given(phoneBookRepository.contains(momContactName))
  .willReturn(false);
 
phoneBookService.register(momContactName, momPhoneNumber);
 
then(phoneBookRepository)
  .should()
  .insert(momContactName, momPhoneNumber);

动态mock

有时候我们可能会遇到这个问题:在方法内部可能有循环或者递归,会多次调用其它对象的方法,但输入的参数不同,我们在mock的时候,期望它根据不同的输入参数返回不同的结果。这个时候如果一个一个写given,就需要写很多次。但其实可以用动态mock来做。它是基于InvocationOnMock来实现的。

given(phoneBookRepository.contains(momContactName))
  .willReturn(true);

given(phoneBookRepository.getPhoneNumberByContactName(momContactName))
  .will((InvocationOnMock invocation) ->
    invocation.getArgument(0).equals(momContactName) 
      ? momPhoneNumber 
      : null);

phoneBookService.search(momContactName);

then(phoneBookRepository)
  .should()
  .getPhoneNumberByContactName(momContactName);

Mockito的不足

Mockito可以说是Java语言最流行的mock框架了。但它不是所有对象都可以mock的,有一些限制。

Mockito在3.4.0以前,是不能mock静态方法的。这取决于它的底层实现,是使用动态代理来做的。而动态代理是代理静态方法、final方法和private方法的。

一般来说,我们不应该mock静态方法、final方法和private方法的。但有时候可能会有这样的需求,比如Apache和guava框架,就使用了大量的静态方法提供一些工具类。如果需要Mock的话,就得配合PowerMock等框架来实现。但PowerMock框架目前还不支持JUnit 5。

在Mockito 3.4.0,开始支持mock静态方法,底层是通过修改字节码来实现的。但性能很差,mock一个实例大概要一秒多,大家酌情使用。

关于作者

我是Yasin,一个有颜有料又有趣的程序员

微信公众号:编了个程

个人网站:https://yasinshaw.com



Tags:mockito   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
Hello 大家好,我是阿粉,日常工作中很多时候我们都需要同事间的相互配合协作完成某些功能,所以我们经常会遇到服务或者应用内不同模块之间要互相依赖的场景。比如下面的场景,...【详细内容】
2021-06-09  Tags: mockito  点击:(127)  评论:(0)  加入收藏
为什么要mock?有很多朋友不愿意写单元测试,觉得写单测试比较花时间,甚至不会写单元测试,很大程度上是因为不想写或者不会写mock。mock对于单元测试来很重要。单元测试之所以名...【详细内容】
2020-08-17  Tags: mockito  点击:(114)  评论:(0)  加入收藏
▌简易百科推荐
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(1)  评论:(0)  加入收藏
程序是如何被执行的&emsp;&emsp;程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(9)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(19)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(23)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(24)  评论:(0)  加入收藏
一个项目的大部分API,测试用例在参数和参数值等信息会有很多相似的地方。我们可以复制API,复制用例来快速生成,然后做细微调整既可以满足我们的测试需求1.复制API:在菜单发布单...【详细内容】
2021-12-14  AutoMeter    Tags:AutoMeter   点击:(20)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条