Mockito单元测试基本使用

news2024/11/26 23:45:35

文章目录

    • 1.为什么需要Mock
    • 2.Mockito 中常用方法
      • 2.1 Mock 方法
      • 2.2 对 Mock 出来的对象进行行为验证和Junit结果断言
      • 2.3 测试桩stub
      • 2.4 参数匹配器
      • 2.5 mock()与spy()
      • 2.6 @InjectMocks

本文参考:

【码农教程】手把手教你Mockito的使用 - 掘金 (juejin.cn)

java - doReturn().when()与when().thenReturn() - 成长之路 - SegmentFault 思否

单元测试实践篇:Mock_阿里巴巴淘系技术团队官网博客的博客-CSDN博客

阿里是如何进行单元测试培训的?_Hollis Chuang的博客-CSDN博客

【Mockito】Mockito + Junit 5 快速入门_哔哩哔哩_bilibili

1.为什么需要Mock

测试驱动的开发( TDD)要求我们先写单元测试,再写实现代码。在写单元测试的过程中,我们往往会遇到要测试的类有很多依赖,这些依赖的类/对象/资源又有别的依赖,从而形成一个大的依赖树,要在单元测试的环境中完整地构建这样的依赖,是一件很困难的事情。如下图所示:
在这里插入图片描述
为了测试类A,我们需要Mock B类和C类(用虚拟对象来代替)如下图所示:
在这里插入图片描述

2.Mockito 中常用方法

先添加maven依赖:

mockito和junit:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.jxz</groupId>
    <artifactId>MockitoLearning</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>4.3.1</version>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>
    </dependencies>

</project>

2.1 Mock 方法

mock 方法来自 org.mockito.Mock,它表示可以 mock 一个对象或者是接口。

public static <T> T mock(Class<T> classToMock)
  • classToMock:待 mock 对象的 class 类。
  • 返回 mock 出来的类

实例:使用 mock 方法 mock 一个List类

List mockList = Mockito.mock(List.class);

也可以使用注解来快速模拟

  1. @Mock+MockitoAnnotations.openMocks(this)

也有用@Mock+MockitoAnnotations.initMocks(this)的

package com.jxz;

import org.junit.Before;
import org.junit.Test;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

import java.util.List;

import static org.mockito.Mockito.verify;
/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/19
 */
public class MockExample1 {
    @Mock
    private List mockList;

    @Before
    public void setup(){
        MockitoAnnotations.openMocks(this);
    }

    @Test
    public void testMock(){
        mockList.add(1);
        verify(mockList).add(1);
    }
}
  1. @Mock+@RunWith(MockitoJUnitRunner.class)
package com.jxz;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import java.util.List;

import static org.mockito.Mockito.verify;
/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/19
 */
@RunWith(MockitoJUnitRunner.class)
public class MockExample2 {
    @Mock
    private List mockList;

    @Test
    public void testMock(){
        mockList.add(1);
        verify(mockList).add(1);
    }
}

2.2 对 Mock 出来的对象进行行为验证和Junit结果断言

一旦mock对象被创建了,Mock对象会记录我们调用的所有交互,也就是各种方法和参数,验证的意思是”查看我们到底有没有调用过mock的这个方法“,Mockito 中验证的方法是:verify

package com.jxz;

import org.junit.Test;
import org.mockito.Mockito;

import java.util.List;

import static org.mockito.Mockito.verify;
/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/19
 */
public class Demo {
    @Test
    public void testMock(){
        List mockList = Mockito.mock(List.class);
        mockList.add(1);
        mockList.add("one");

        // verify
        verify(mockList).add(1);
//        verify(mockList).add("two"); //会抛出异常
    }
}

JUnit断言使用到的类是 Assert.

@Test
public void testMock2(){
    List mockList = Mockito.mock(List.class);
    mockList.add(1);

    Assert.assertEquals(1,mockList.get(1));
}

输出:

java.lang.AssertionError: 
Expected :1
Actual   :null

当使用 mock 对象时(这里是mockList),如果不对其行为进行定义(也就是下面的打桩),则 mock 对象方法的返回值为返回类型的默认值(这里为null)。

2.3 测试桩stub

指定mock对象的交互行为逻辑,基本格式when().thenReturn()

when(mockedList.get(0)).thenReturn(“first”)规定当调用get(0)的时候返回"first"

@Test
public void testStub(){
    LinkedList mockList = Mockito.mock(LinkedList.class);
    // stubbing
    when(mockList.get(0)).thenReturn("first");
    when(mockList.get(1)).thenThrow(new RuntimeException());

    System.out.println(mockList.get(0)); //first
    System.out.println(mockList.get(1)); //RuntimeException
}

当我们连续两次为同一个方法使用stub的时候,他只会只用最新的一次。一旦这个方法被stub了,就会一直返回这个stub的值。如下:

@Test
public void testStub2(){
    LinkedList mockList = Mockito.mock(LinkedList.class);
    when(mockList.get(0)).thenReturn("first");
    when(mockList.get(0)).thenReturn("second");
    System.out.println(mockList.get(0));
    System.out.println(mockList.get(0));
}

输出:

second
second

还有一种测试桩指定的方式,即doReturn().when(),本质上也是规定行为。

@Test
public void testDoReturn(){
    A a = new A();
    A mockA = Mockito.mock(A.class);
    Mockito.when(mockA.add(1,2)).thenReturn(5); // 当mockA调用add(1,2),返回5
    System.out.println(mockA.add(1,2)); // 5

    A A2 = new A();
    A mockA2 = Mockito.mock(A.class);
    Mockito.doReturn(5).when(mockA2).add(1,2); // 同样是当mockA调用add(1,2),返回5
    System.out.println(mockA2.add(1,2)); // 5
}

两者本质上就是一个由于执行顺序产生的问题,Mockito.when(mockA.add(1,2)).thenReturn(5),会先执行a + b,即 1 + 2 , 结果本应是3,但由于后面的thenReturn,所以调用该方法时,实际的返回值是5。而Mockito.doReturn(5).when(mockA2).add(1,2)就不会执行a+b的操作,而会直接返回5。区别就是若是一定会执行add()方法,难免会产生无法预估的副作用,比如抛出异常等。

2.4 参数匹配器

参数匹配器可以进行参数的灵活指派。

@Test
public void testMatch(){
    Map mockMap = Mockito.mock(Map.class);
    // 正常打桩测试
    when(mockMap.get("key1")).thenReturn("value1");
    System.out.println(mockMap.get("key1")); //value1

    // 任意String的参数匹配器
    when(mockMap.get(anyString())).thenReturn("value2");
    System.out.println(mockMap.get(anyString())); // value2
    System.out.println(mockMap.get("key2")); // value2
    System.out.println(mockMap.get(1)); // null

    // 多个入参时,要么都使用参数匹配器,要么都不使用,否则会异常, put()返回参数
    when(mockMap.put(anyString(),anyInt())).thenReturn("value3");
    System.out.println(mockMap.put("key3",3)); // value3
    System.out.println(mockMap.put(anyString(),anyInt())); // value3
//        System.out.println("key3",anyInt()); // 异常

    // verify也支持参数匹配
    verify(mockMap,atLeastOnce()).get(anyString()); // 前面交互至少调用了一次get(anyString())
    verify(mockMap).put(anyString(),eq(3)); // 前面至少调用了一次put(anyString(),3)
}

2.5 mock()与spy()

  1. 被 spy 的对象会走真实的方法,而 mock 对象走虚拟对象的方法,返回默认值
  2. spy() 方法的参数是对象实例,mock()方法 的参数是 class

示例:

package com.jxz;

import org.junit.Test;
import org.mockito.Mockito;

/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/20
 */
public class SpyAndMock {

    @Test
    public void testSpy(){
        A a = new A();
        A a1 = Mockito.mock(A.class);
        A a2 = Mockito.spy(a);
        System.out.println(a1.add(1,2));
        System.out.println(a2.add(1,2));
    }
}

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

输出:

0
3

通过mock生成的对象,会拥有以前的对象的所有方法,但是方法中都没有了功能,就比如上面的a1对应的类可以理解下面这样

A1 extend A {  
    pubic int add(int a, int b) {  
        return 0;  
  }  
}

2.6 @InjectMocks

@InjectMocks和@Mock配合可以简化某个类中,注入类的配置。同时可以在外面对被@Mock的类进行行为指定,从而让其被调用时,产生我们指定的结果。

示例:

要测试的类和方法:

package com.jxz;

/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/20
 */
public class RegistrationImpl {
    SalesDao salesDao = new SalesDao();
    SaveDao saveDao = new SaveDao();
    public String register(int id, String name){
        String result1 = salesDao.findRep(name);
        System.out.println(result1);

        String result2 = saveDao.save(id, name);
        System.out.println(result2);

        return result1 + "_" + result2;
    }
}

class SalesDao{
    public String findRep(String name){
        return name;
    }
}

class SaveDao{
    public String save(int id, String name){
        return id + name;
    }
}

对应的测试类和方法:

package com.jxz;

import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;

import static org.mockito.Mockito.when;
/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/20
 */
@RunWith(MockitoJUnitRunner.class)
public class RegistrationImplTest {
    @InjectMocks
    private RegistrationImpl registrationImpl; // 需要被注入mock对象的对象
    @Mock
    private SalesDao salesDao; // RegistrationImpl中注入的类
    @Mock
    private SaveDao saveDao;

    @Test
    public void testRegister(){
        // 进行mock注入类的行为指定
        // 可以看到register方法中调用mock对象打印出来的东西正确
        when(salesDao.findRep("jiangxuzhao")).thenReturn("jiangxuzhao666");  // jiangxuzhao666
        when(saveDao.save(123,"jiangxuzhao")).thenReturn("123jiangxuzhao666"); // 123jiangxuzhao666

        String result = registrationImpl.register(123, "jiangxuzhao");
        Assert.assertEquals(result,"jiangxuzhao666_123jiangxuzhao666"); // 结合mock对象的返回值正确
//        Assert.assertEquals(result,"null"); // org.junit.ComparisonFailure
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/665371.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

利用SD的插件从文本或者图片创建3D模型

利用SD的插件从文本或者图片创建3D模型 一个用于自动从文本或图像生成3D模型的稳定扩散WebUI的自定义扩展。 利用 OpenAl Shap-E 从文本或图像生成 3D 模型&#xff0c; GitHub项日地址: https://github.com/jtydhr88/sd-webui-txt-img-to-3d-model 安装步骤: 在扩展中心选…

Linux之LVM模式下LV和VG扩容

一、LVM简介 LVM (Logical Volume Manager) 是一个逻辑卷管理器&#xff0c;它允许用户将多个硬盘分区或者整个硬盘组成一个或多个逻辑卷。LVM 可以在运行时动态地改变逻辑卷的大小&#xff0c;而不需要关机或重新启动系统。它也可以将多个硬盘的存储空间组合在一起&#xff0c…

OpenGL视口学习

VC6新建一个openGL类型项目&#xff1b; 出现一些选项&#xff0c;默认后生成一个项目&#xff1b; VC6自带GL支持&#xff1b; 需要添加附加包含路径&#xff1b; 而后构建工程&#xff1b;运行如下&#xff1b; 是一个旋转的立方体&#xff1b; 生成的代码不是MFC的&#xff…

Python入门教程+项目实战-13.4节-程序实战-二分查找算法

目录 13.4.1 何为二分查找&#xff1f; 13.4.2 算法实现 13.4.3 系统学习python 13.4.1 何为二分查找&#xff1f; 我们已经学完了Python中的字典和集合&#xff0c;利用字典和集合就可以实现快速查找&#xff0c;非常方便。字典与集合使用了哈希表的索引结构来加快查找&am…

C++ 设计模式----“对象创建“模式

“对象创建”模式  通过“对象创建” 模式绕开new&#xff0c;来避免对象创建&#xff08;new&#xff09;过程中所导致的紧耦合&#xff08;依赖具体类&#xff09;&#xff0c;从而支持对象创建的稳定。它是接口抽象之后的第一步工作。  典型模式 • Factory Method •…

【Java】Java核心要点总结 68

文章目录 1. 为什么重写 equals() 时候必须重写 hashCode()2. 字符串常量池3. 字符串中的 intern() 方法4. try-catch-finally5. finally 中的代码不一定会执行 1. 为什么重写 equals() 时候必须重写 hashCode() 因为两个相等的对象的 hashCode 值必须是相等。也就是说如果 equ…

三、动画 -变形transform

目录&#xff1a; 1.变形transform定义 2.具体描述 3.用途 4.练习 一、变形transform定义 变形就是指通过CSS来改变元素的形状或位置 变形不会影响到页面的布局transform 用来设置元素的变形效果设置具体的值用translate()函数 二、具体描述 - 平移&#xff1a;translateX() 沿…

2023 3de实时仿真环境下资源不可见或没有了(只有floor)

F3退出实时仿真环境&#xff0c;双击结构树父节点 之后再进入实时仿真&#xff0c;3d仿真&#xff0c;再打开资源就可以看到了。

【雕爷学编程】Arduino动手做(117)---P10V706LED屏模组

37款传感器与执行器的提法&#xff0c;在网络上广泛流传&#xff0c;其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块&#xff0c;依照实践出真知&#xff08;一定要动手做&#xff09;的理念&#xff0c;以学习和交流为目的&am…

【Atlas 200DK A2体验】Atlas 200DK A2初体验记录

开箱照 烧录镜像 Windows版制卡工具下载地址&#xff1a; https://ascend-repo.obs.cn-east-2.myhuaweicloud.com/Atlas%20200I%20DK%20A2/DevKit/tools/latest/Ascend-devkit-imager_latest_win-x86_64.exe 准备一张MicroSD卡&#xff0c;个人建议容量至少在64GB以上 一键制…

6 月份有哪些 GitHub 项目火了?

本期推荐开源项目目录&#xff1a; 1. 谁在招人&#xff1f; 2. ChatGPT-Midjourney 3. 让 AI 给你写代码 4. 免费的 OpenAI GPT-4 的 API 5. 金融领域的大模型 6. 文本生成音乐模型 01 谁在招人&#xff1f; 阮一峰在 GitHub 上维护了一个《科技爱好者周刊》&#xff0c;每周五…

开源的可视化爬虫易采集EasySpider:如何无代码可视化的爬取需要登录才能爬的网站

一个可视化爬虫软件&#xff0c;可以使用图形化界面&#xff0c;无代码可视化的设计和执行爬虫任务。只需要在网页上选择自己想要爬的内容并根据提示框操作即可完成爬虫设计和执行。同时软件还可以单独以命令行的方式进行执行&#xff0c;从而可以很方便的嵌入到其他系统中。 …

AI导师、AI提示工程师 # Earth实现任意角色设定

‍‍ 如何通过学习优秀的开源项目&#xff0c; 设定一个任意角色 opus 嗨&#xff0c;Shadow&#xff0c;我想学习如何给ChatGPT设定一个角色&#xff0c;可以教我吗&#xff1f; 当然可以&#xff0c;我们可以通过学习优秀的开源项目来了解实现细节。 shadow 其中&#xff0c;…

SQLmap使用教程图文教程(超详细)

「作者主页」&#xff1a;士别三日wyx 「作者简介」&#xff1a;CSDN top100、阿里云博客专家、华为云享专家、网络安全领域优质创作者 「推荐专栏」&#xff1a;对网络安全感兴趣的小伙伴可以关注专栏《网络安全入门到精通》 SQLmap 一、目标1、指定url2、指定文件&#xff08…

ICLR Oral总分世界第五(10,8,8):可学习的行为控制,超越DeepMind和OpenAI突破Atari人类世界纪录...

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 作者介绍 范嘉骏 清华大学计算机技术专业二年级研究生&#xff0c;主要研究深度强化学习 报告题目 可学习的行为控制&#xff0c;超越DeepMind和OpenAI突破Atari人类世界纪录 内容简介 探索问题一直是深度强化学…

【kafka】kafka基础架构

文章目录 1、kafka简介2、kafka的特性3、kafka的应用场景4、kafka架构&#xff08;重点&#xff09;4.1、broker4.2、topic4.3、partition4.4、offset4.5、producer4.6、consumer4.7、consumer group4.8、leader4.9、follower4.10、rebalance 5、对kafka架构的几点解释6、几种M…

突破软件测试的瓶颈

软件测试两三年&#xff0c;编程能力半生不熟&#xff0c;三流学校出身&#xff0c;E很差&#xff0c;工作主要还是写各种测试文档和手工测试&#xff1b;相信不少测试人员都和这情况相似&#xff1b;下面就这几个方面谈一下自己的看法。 关于编程的能力 之前有同学问我测试要学…

青翼自研-模块化互联产品 • 模拟采集FMC子卡产品资料

FMC123是一款基于FMC标准规范&#xff0c;实现2路14-bit、3GSPS ADC采集功能、2路16-bit 12.6GSPS回放子卡模块。该模块遵循VITA57.1标准&#xff0c;可直接与FPGA载卡配合使用&#xff0c;板卡ADC器件采用ADI公司的AD9208芯片&#xff0c;&#xff0c;与ADI公司的AD9689可以实…

C国演义 [第五章]

第五章 子集题目理解步骤树形结构递归函数递归结束的条件单层逻辑 代码 子集II题目理解步骤树形结构递归函数递归结束的条件单层逻辑 代码 子集 力扣链接 给你一个整数数组 nums &#xff0c;数组中的元素 互不相同 。返回该数组所有可能的子集&#xff08;幂集&#xff09;。…

MySQL索引、事务、与存储引擎

MySQL索引、事务、与存储引擎 一、索引 1.概念 索引是一个排序的列表&#xff0c;包含索引字段的值和其相对应的行数据所在的物理地址2.作用 优点 加快表的查询速度 可以对字段排序缺点 额外占用磁盘空间 更新包含索引的表效率会更慢3.索引工作模式 没有索引的情况下&am…