单元测试Mockito使用及详解

2023-10-27

一、什么是MOCK测试
Mock 测试就是在测试过程中,对于某些不容易构造(如 HttpServletRequest 必须在Servlet 容器中才能构造出来)或者不容易获取比较复杂的对象(如 JDBC 中的ResultSet 对象),用一个虚拟的对象(Mock 对象)来创建以便测试的测试方法。
Mock 最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。

mock中的必知概念:

桩函数(stub):桩函数实际上是白盒测试中的概念,意思是使用一些自己定义的测试函数来替换当前需要测试的函数。被替换的函数可能是目前还没写完的,这样能够加速开发,或更好的找错误源。

打桩(存根):模拟要调用的函数(打桩对象),给它提供桩函数,给桩函数返回一个值。简单的说自定义输入输出,不打桩默认返回null。

mock和stub:
相同点:Stub和Mock对象都是用来模拟外部依赖,使我们能控制。
不同点:而stub完全是模拟一个外部依赖,用来提供测试时所需要的测试数据。而mock对象用来判断测试是否能通过,也就是用来验证测试中依赖对象间的交互能否达到预期。在mocking框架中mock对象可以同时作为stub和mock对象使用,两者并没有严格区别。

二、Mockito使用
本文用maven作例子,导入jar包

<dependencies>
        <!-- junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.8.2</version>
        </dependency>
            <!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
            <dependency>
                <groupId>org.mockito</groupId>
                <artifactId>mockito-core</artifactId>
                <version>2.23.4</version>
                <scope>test</scope>
            </dependency>
    </dependencies>

在这里插入图片描述

 1.声明mockito对象

有两种方式,但首先都得导入import static org.mockito.Mockito.*; 最好用静态导入,可以直接调用方法

通过@Mock注解声明mock对象。MockitoJUnitRunner(或者Mockito.initMocks(this))为@mock,@spy等注解提供了初始化作用,所以用到注解时,一般都要使用它。
@Mock: 创建一个Mock.
@InjectMocks: 创建一个实例,简单的说是这个Mock可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中。可以调用类中的真实方法。

//让注解生效的第一种方法
@RunWith(MockitoJUnitRunner.class)
public class TestMocks {
	//@Mock注解会自动mock一个list对象
    @Mock
    List mock;
    
	//让注解生效的第二种方法,二选一即可
 	@Before
    public void setup() {
        MockitoAnnotations.initMocks(this);
    }
    
    @Test
    public void testMethod(){
    mock.add("firstTime");
    mock.add("secondTime");
    }
}

直接声明mock对象

public class TestMock {
    //声明一个List的mock对象
    List mock=mock(List.class);

    @Test
    public void testMethod(){
    mock.add("firstTime");
    mock.add("secondTime");
    }
}

测试过程中常用注解:

@RunWith 指定运行环境,例:

@RunWith(SpringRunner.class) Junit运行Spring的测试环境

@RunWith(MockitoJUnitRunner.class) Junit运行Mockito的运行环境,不会加载springboot上下文

@SpringBootTest 加载springboot上下文配置

@WebMvcTest(xxxController.class) 用于测试单个Controller,提供了自配置的MocMvc,可以不启动整个应用就可以以http请求的方 式测试Controller,不过这个注解不会去扫描@Service,@Mapper等注解,当调用这些服务时最好提前打桩,不然会报Error create bean这类异常

@Mock 用于模拟一个服务,例如测试的时候需用调用B服务,但是B服务没写 好,这是可以先Mock一个B服务

@InjectMocks 标注服务的实现类,创建一个实例,其余用@Mock注解创建的mock将被注入到用该实例中

@Before 每个测试方法执行前,执行一次

@After 每个测试方法执行完,执行一次

@Test 标注测试方法

2.mock对象的when存根方法 

就是前面提到的打桩,一般用于给mock对象的函数指定自己需要的输入输出值,示例:

List mock=mock(List.class);
 @Test
    public void testMethod(){
    mock.add("firstTime");
    mock.add("secondTime");
    //打桩
    when(mock.get(0)).thenReturn(1);
    //如果是没有返回值的void方法,直接
    when(method).notify();
    }

表示mock.get(0)返回值为1,这个地方的0也可以替换成anyInt()函数,代表获取任意数字返回都为0,不打桩默认返回为null。
注意:对于 static 和 final 方法, Mockito 无法对其 when(…).thenReturn(…) 操作。

3.mock的doNothing方法

不做任何返回

List mock=mock(List.class);
  @Test
    public void testMethod(){
        //打桩,返回值为1
        doNothing().when(mock.get(1));
    }


4.mock的verify方法

一般用于检查是否调用了某些指定的方法。简单来说, 它可以验证一次测试中发生的某些行为。在测试代​​码的底部使用它来确保调用定义的方法。同时也可以测试方法的调用次数,当不使用times函数试,默认为检查一次,times(num),num为调用检查的次数。

List mock=mock(List.class);
 @Test
    public void testMethod(){
        //打桩,返回值为1
        when(mock.get(0)).thenReturn(1);
        //此处调用了mock.get(0)方法一次
        assertEquals("测试一下",mock.get(0),1);
        //验证是否调用一次
        verify(mock).get(0);
        //加上times(num)函数,检查是否调用num次
        verify(mock,times(2)).get(0);
    }

mock除了times(num)方法以外,还提供了几种更加方便的方法:
never() 没有被调用,相当于 times(0)
atLeast(N) 至少被调用 N 次
atLeastOnce() 相当于 atLeast(1)
atMost(N) 最多被调用 N 次

随后我们可以使用verifyNoMoreInteractions方法来验证是否该mock对象所有的交互都得到验证

verifyNoMoreInteractions(mock);

如果有哪一次的调用没有验证,会报错提示
5.mock的thenThrow方法

用于存根void方法以引发异常时使用。它为每个方法调用创建一个新的异常实例。

List mock=mock(List.class);
 @Test
    public void testMethod(){
        //打桩,返回值为1
        when(mock.get(0)).thenReturn(1);
        //调用get(0)时自定义异常
        doThrow(new Exception("出错啦")).when(mock).get(0);
    }

注意: thenThrow和doThrow作用都是抛出异常,用 doThrow 可以让返回void的函数抛出异常,而thenThrow不可以,因为when的参数是非void,doThrow语法:

doThrow(new RuntimeException("异常")).when(mock).hello();


6.mock的spy函数

spy 的意思是你可以修改某个真实对象的某些方法的行为特征。

List list = new LinkedList();  
List spy = spy(list);  
  
//optionally, you can stub out some methods:  
when(spy.size()).thenReturn(100);  
   
//using the spy calls <b>real</b> methods  
spy.add("one");  
spy.add("two");  
   
//prints "one" - the first element of a list  
System.out.println(spy.get(0));  
   
//size() method was stubbed - 100 is printed  
System.out.println(spy.size());  
   
//optionally, you can verify  
verify(spy).add("one");  
verify(spy).add("two");  


这里改掉了list的size方法,如果我们再声明对象spy后添加when(spy.get(0)).thenReturn(“foo”); 就会抛出空指针异常,因为用spy之后,在我们的原始对象中,list.get(0)是没有值的,不能直接存根,所以使用spy的时候打桩我们尽量使用doReturn方法,如果用mock的话,都是虚假函数,不会执行真正的函数部分。

7.mock的thenAnswer函数

虽然doReturn函数可以帮助我们返回想要的值,但是有时候根据业务逻辑,我们需要对参数进行判断,返回不同的值,怎么办呢。这时候就可以用thenAnswer函数。此接口允许通过InvocationOnMock参数与参数进行交互.此外,answer方法的返回值将是模拟方法的返回值。

List mock=mock(List.class);
 @Test
    public void testMethod(){
        when(mock.get(anyInt())).thenAnswer(new Answer<Object>() {
            public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
            //获取参数值
                Object[] args = invocationOnMock.getArguments();
                Integer a= (Integer) args[0];
                //根据参数值返回不同的数值
                if(a == 1){
                    return 99;
                }else {
                    return 10;
                }
            }
        });


注意: thenAnswer和doAnswer作用是一样的,只是使用方法(语法)不同,doAnswer语法:

doAnswer(new Answer.....//方法和上面一样).when(mock).method();


8.mock的doCallRealMethod方法
thenCallRealMethod 可以用来重置 spy 对象的特定方法特定参数调用。例如我们声明了一个spy对象,但是spy对象调用的是函数的真实的方法,如果我们给spy对象赋了新的值,但是我们又想要原来对象真实的值,这时候用doCallRealMethod方法重置spy对象,恢复真实值。示例:
调用类:

public class TestClass {
    public int add(int a,int b){
        return a+b;
    }
}


测试方法:

   

@Spy
    TestClass test;
    @Before
    public void setup() {
        //让注解生效
        MockitoAnnotations.initMocks(this);
    }
 	@Test
    public void testMethods(){
    assertEquals(3,test.add(1,2));
    //给1+2赋值为100
    when(test.add(1,2)).thenReturn(100);
    System.out.println(test.add(1,2));
    //调用真实值
    doCallRealMethod().when(test).add(1,2);
    System.out.println(test.add(1,2));
    }


输出结果:

在这里插入图片描述

注意::和thenCallMethod用法一样,只不过语法有不同:

when(mock.method).thenCallMethod();


9.mock的reset方法

使用 reset 方法,可以重置之前自定义的返回值和异常。
用法:reset(对象)
例子:

@Test
    public void test() {
        List list= mock(List.class);
         // mock 对象方法的默认返回值是返回类型的默认值,默认值为null
        assertEquals(null, list.get(0));
        when(list.get(0)).thenReturn(100);
        // 设置让 list.get(0)返回 100
        // 重置 mock 对象,list.get(0)返回 null
        reset(list);
        assertEquals(null, list.get(0));
    }


这里注意,如果reset(spy对象)的话,其实和doCallRealMethod方法有异曲同工之妙,最后都会调用真实的方法,即重置spy对象。

三、总结
通过以上使用,我们大致可以了解到mock对我们测试的具体帮助是什么,还有它的基本使用,当然它还有其它方法,只了解了一些最常用的测试方法,需要更深入了解还需要我们在写代码的过程中去探索,mock大大简化了我们写单元测试的复杂度,一些难引用的对象都可以通过mock来模拟。

小结:mock中doMethod和thenMethod区别,例如doThrow和thenThrow(当然这两个不止语法有区别,用法也有点区别,详情看上面的doThrow,其它方法基本就是语法上的差异比如doReturn和thenReturn等):
语法:

List list=mock(List.class);
when(list.get(0)).thenThrow(new Exception());
//等同于(这两个稍有区别)
doThrow(new Exception()).when(list).get(0);

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

单元测试Mockito使用及详解 的相关文章

  • 如果我在 JSP 中有 html 元素,那么执行顺序是什么?

    什么将执行第一个 body 元素或 head 元素 Head Body scriplet 如果我明白您的要求 JSP 文件中的每个元素都会按照代码从上到下出现的顺序进行处理
  • 超立方体错误。非法的最小或最大规格

    尝试从这里运行示例代码http tess4j sourceforge net codesample html http tess4j sourceforge net codesample html我收到一条错误消息 Error Illega
  • Spring MVC 应用程序可以是多线程的,即使它的 servlet 不是吗?

    当您谈论 Spring 应用程序是多线程时 您是否一定是指该应用程序中定义的 servlet 是否是多线程的 或者即使应用程序中的 servlet 不是多线程 Spring 应用程序也可以配置为多线程吗 不再支持单线程 servlet 它们
  • jUnit 中每个 @Test 的不同拆卸

    有没有办法为 jUnit 中的每个 Test 定义不同的拆卸 Use the After注释来指示每个之后要运行的方法 Test 像这样的全套注释是 BeforeClass 首先 Tests are run Before 在每个之前 Tes
  • 通过代理从java发送电子邮件

    我使用 Java Mail API 来发送和接收电子邮件 现在我做这个项目的地方有一个代理服务器 我可以知道如何通过代理服务器从java发送电子邮件吗 请参阅此处的常见问题解答 http www oracle com technetwork
  • 使用Keycloak保护Tomcat应用程序时出现HTTP 403禁止错误

    我为这个错误苦苦挣扎了一整天 我一遍又一遍地检查我在tomcat中Keycloak和APP的配置 没有发现错误 下图为测试场景 APP配置 1 Keycloak json是从Keycloak控制台复制的 2 context xml 也正确
  • 从 java.util.TimeZone 转换为 org.joda.DateTimeZone

    在Java中如何将一个实例转换为java util TimeZone to org joda DateTimeZone并保持夏令时 Joda Time 处于维护模式 The 乔达时间 http www joda org joda time
  • 如何知道 Solr Optimize 何时完成?

    我正在使用 Solr php client 通过 php 与 Solr 进行通信 这段代码触发solr优化命令 solr gt optimize 请问有没有什么方法可以确定优化完成了 这都是因为我的网站上有一个管理页面 我每天必须手动优化
  • 查找前 N 个五边形数

    我必须找到第一个N pentagonal numbers 1 从 1 100 并每行显示 10 个 我必须使用getPentagonalNumber int n 方法也是如此 显然这就是它存在的原因 到目前为止 这是我的代码 package
  • 测试正确的时区处理

    我们正在处理大量数据 所有数据均以 UTC Java 语言 标记 在读取这些数据 将其存储在数据库中以及再次将其取出之间 发生了一些数据在夏令时期间关闭一小时的情况 由于 UTC 没有夏令时的概念 这显然是软件中的一个错误 一旦知道 就很容
  • 使用 Gradle 构建 Kotlin + Java 9 项目

    我对 Gradle 老实说 还有 Java 9 相当陌生 我正在尝试使用 Gradle 构建一个混合了 Java 9 和 Kotlin 的简单库项目 更详细地说 Java中有一个接口 Kotlin中有一个实现 我会用 Kotlin 做所有事
  • 在 Scala 中创建 Java 对象

    我有一个 Java 类 Listings 我在 Java MapReduce 作业中使用它 如下所示 public void map Object key Text value Context context throws IOExcept
  • IntelliJ 对于 Java 项目使用的默认构建过程是什么?

    直接从 IntelliJ 中的 IDE 构建 Java 项目非常好 它速度很快 而且很有效 我无法找到任何有关 IntelliJ 如何进行这些默认构建的文档 我猜它使用Ant 我想做的是为下载我的项目的任何人自动化这个快速 轻松的构建过程
  • HttpMediaTypeNotAcceptableException / HttpMediaTypeNotAcceptableException:找不到可接受的表示

    我有一个客户端正在尝试连接的 API 但是它会抛出错误 2015 09 22 04 21 44 297 org springframework web servlet mvc method annotation HttpEntityMeth
  • spring-hibernate 花费更多时间的任何原因?

    目前 我正在春季和冬眠期间从事一个项目 我来到这里 获取记录并在 JSP 中显示这些记录需要更多时间 我在各处都保留了时间戳 以查看哪里花费了更多时间 Time HomeController start 2014 07 09 18 58 5
  • Spring 4 MVC 和 Websockets - 没有合适的默认 RequestUpgradeStrategy

    我需要 Websockets 在我的应用程序中进行实时更新 所以我找到了这个例子并一步一步地做到了here http raymondhlee wordpress com 2014 01 19 using spring 4 websocket
  • 选择活动时运行时崩溃

    首先我想说我几乎没有 Android 经验 这是我在 Android 中的第一个项目 而且我的老师不太擅长教学 所以我对任何过度的无知表示歉意 在进一步讨论之前先解释一下 我的应用程序的目标本质上是能够记录您在某些活动上花费了多少时间 记录
  • Selenium 查看鼠标/指针

    有什么方法可以在运行测试时真正看到硒鼠标吗 要么是 Windows 光标图像 要么是某种点或十字线或任何东西 我正在尝试使用拖放功能selenium and java in an HTML5Web 应用程序 并且能够看到光标以了解它实际在做
  • 无法读取使用 DataOutputStream 发送的号码

    这是我的客户端代码 Random rand new Random int n rand nextInt 50 1 DataInputStream dis new DataInputStream socket getInputStream D
  • 当列表中不存在 X 时,从列表中查找大于 X 的值

    我试图从列表中查找大于特定值 在我的情况下已知 的值 Example Given list 1 2 5 10 15 list is sorted 查找大于的值X 7在这种情况下 期望的结果 返回一个包含值的列表 10 15 我尝试使用jav

随机推荐

  • IDEA产生及相应的应用

    思考 在用记事本编写程序代码的过程中 需要配合使用各种命令在控制台调控编译 给我们的实际操作带来了不小的工作量 如果初学者不能够调整好心态一步步操作 很容易放弃 如何能够让初学者和开发者用起来更方便快捷呢 深度的思考就会有创新 由此一个新的
  • 区块链开发指南_区块链软件开发详解

    开发业务层区块链应用 Hyperledger fabric目前支持java nodejs go和python语言的sdk 供用户选择用不同的语言开发上层应用 使用相应的sdk调用部署在区块链上的链码 以下示例中我们选择使用go语言开发业务层
  • 《Python》计算机视觉编程

    基本的图像操作处理 PIL 目前pycharm使用的是pillow库 from PIL import Image pil im Image open empire jpg 上述代码的返回值pil im是一个PIL图像对象 图像的颜色转换可以
  • Cisco模拟器-ospf配置实验(验证与负载均衡)

    要求 如图运行OSPF区域 区域1要求用OSPF明文验证 区域0要求用可靠的验证方式 要求R4去往R1的环回从R2走 R1上看到去往R4的两条等价负载均衡 基础配置 R1 R1 config int f0 0 R1 config if ip
  • django2.x/3.x 前端页面在debug模式中找不到动态文件static

    修改setting配置 STATIC URL static STATICFILES DIRS os path join BASE DIR static html页面中 load static bootstrap bootstrap min
  • objdump说明

    objdump有点象那个快速查看之流的工具 就是以一种可阅读的格式让你更多地了解二进制文件可能带有的附加信息 使用objdump显示vxWorks的文件头信息以及入口地址例子 D ftproot gt C Tornado2 2Arm hos
  • css文本超出容器宽度自动换行及超过行数加省略号...

    css文本超过div的宽度时 让它进行自动换行 并且超过div高度时候 在最后一行加省略号 废话不多说 上代码 display webkit box overflow hidden text overflow ellipsis word w
  • 基站分布图,通过基站定位数据来看google基站数据分布

    原文地址 http hi baidu com ant mobile blog item 6d619b11042fd2f7c2ce7950 html 无论是应用更广泛的gps定位 还是基站定位 Wi Fi定位 都需要依托于强大精确的地图 而g
  • STM32CubeIDE使用笔记(02):STM32CubeMX配置与代码生成

    文章目录 目的 用户界面 引脚输出和配置 时钟配置 项目管理 工具 代码生成 更换芯片 总结 目的 STM32CubeIDE中整合了STM32CubeMX用来配置芯片资源生成初始化代码 本篇将介绍STM32CubeMX使用方法 用户界面 界
  • pycharm如何打开历史_分享Pycharm中一些不为人知的技巧

    工欲善其事必先利其器 Pycharm 是最受欢迎的Python开发工具 它提供的功能非常强大 是构建大型项目的理想工具之一 如果能挖掘出里面实用技巧 能带来事半功倍的效果 以下操作都是基于 Windows 平台下的默认KeyMap设置 在
  • MongoDB局域网访问

    在安装目录下的配置文件中修改IP 执行命令 mongod config C Program Files MongoDB Server 4 4 bin mongod cfg logpath E MangoDB log mongod log l
  • Spring面试题

    1 Spring是什么 1 Spring是 个轻量级的控制反转和 向切 的容器框架 2 通过控制反转 IoC 的技术达到松耦合的 的 3 包含并管理应 对象 Bean 的配置和 命周期 这个意义上是 个容器 4 可以整合多种技术 2 谈谈你
  • 现金额大写转换函数

    现金额大写转换函数 ecDo upDigit 168752632 result 人民币壹亿陆仟捌佰柒拾伍万贰仟陆佰叁拾贰元整 ecDo upDigit 1682 result 人民币壹仟陆佰捌拾贰元整 ecDo upDigit 1693 r
  • springboot 项目控制台输出数据库数据

    springboot 项目控制台输出数据库数据 项目目录 修改application properties文件 连接数据库和本地服务器 数据库表及数据 Mapper层 package com example poem mapper impo
  • Pytorch学习笔记--常用函数torch.optim.SGD()总结3

    1 torch optim SGD 函数拓展 import torch LEARNING RATE 0 01 梯度下降学习率 MOMENTUM 0 9 冲量大小 WEIGHT DECAY 0 0005 权重衰减系数 optimizer to
  • maya中的场景资产管理sceneAssembly

    目录 简介 为什么要使用 作用 使用方法 简单的流程介绍 简介 这是maya中的一个流程资产管理插件 用于大场景的整合 内部的工作原理有点类似是reference的封装 为什么要使用 在生产制作中 一个好的流程 都是各个环节并行的 但是上游
  • 在Spring Security中,如何重写AuthenticationProvider类的authenticate方法,以接收json格式的登录请求...

    在Spring Security中 可以通过实现AuthenticationProvider接口并覆盖其authenticate 方法来重写AuthenticationProvider类 以接收JSON格式的登录请求
  • PIL,cv2读取类型及 numpy,tensor格式转换

    PIL读取图片格式以及数据转换操作 PIL库读取的图片格式是 H x W x C 格式的 比如一张图是128x128x3 图片格式PIL PngImagePlugin PngImageFile 通过numpy array img 或者num
  • css元素居中的方法

    目录标题 水平居中 1 给元素设置margin 0 auto 2 margin 负值法 3 transform 4 table布局 5 flex布局 6 inline block 垂直居中 1 margin auto 2 margin 负值
  • 单元测试Mockito使用及详解

    一 什么是MOCK测试 Mock 测试就是在测试过程中 对于某些不容易构造 如 HttpServletRequest 必须在Servlet 容器中才能构造出来 或者不容易获取比较复杂的对象 如 JDBC 中的ResultSet 对象 用一个