基于mockito做单元测试

news2024/9/22 6:50:24

1.简介

  • 配合断言使用(杜绝System.out)
  • 可重复执行
  • 不依赖环境
  • 不会对数据产生影响
  • Spring的上下文环境不是必备的
  • 一般都配合mock类框架对数据库进行隔离

mock类使用场景:

要进行测试的方法存在外部依赖(DB,Redis,第三方接口),为了专注于对该方法的逻辑进行测试,就希望能隔离出来外部依赖,避免外部依赖成为单测的阻塞项,一般单测都是在service进行测试

创建mock对象的三种方法:

/**
 * 创建mock对象的第一种方法
 */
@ExtendWith(MockitoExtension.class)
public class InitMockOrSpyMethod1 {
    @Mock
    private UserService mockUserService;
    @Spy
    private UserService spyUserServices;

    @Test
    public void test1(){
        System.out.println("Mockito.mockingDetails(mockUserService).isMock():"+Mockito.mockingDetails(mockUserService).isMock());
        System.out.println("Mockito.mockingDetails(mockUserService).isSpy():"+Mockito.mockingDetails(mockUserService).isSpy());
        System.out.println("Mockito.mockingDetails(spyUserServiceS).isMock():"+Mockito.mockingDetails(spyUserServices).isMock());
        // spy对象是另一种类型的mock对象
        System.out.println("Mockito.mockingDetails(spyUserServiceS).isSpy():"+Mockito.mockingDetails(spyUserServices).isSpy());
    }

}
/**
 * 创建mock对象的第二种方法
 */
public class InitMockOrSpyMethod2 {
    private UserService mockUserService;
    private UserService spyUserServices;

    @BeforeEach
    public void init(){
        mockUserService = Mockito.mock(UserService.class);
        spyUserServices = Mockito.spy(UserService.class);
    }

    @Test
    public void test1(){
        System.out.println("Mockito.mockingDetails(mockUserService).isMock():"+Mockito.mockingDetails(mockUserService).isMock());
        System.out.println("Mockito.mockingDetails(mockUserService).isSpy():"+Mockito.mockingDetails(mockUserService).isSpy());
        System.out.println("Mockito.mockingDetails(spyUserServiceS).isMock():"+Mockito.mockingDetails(spyUserServices).isMock());
        // spy对象是另一种类型的mock对象
        System.out.println("Mockito.mockingDetails(spyUserServiceS).isSpy():"+Mockito.mockingDetails(spyUserServices).isSpy());
    }
}
/**
 * 创建mock对象的第三种方法
 */
public class InitMockOrSpyMethod3 {
    @Mock
    private UserService mockUserService;
    @Spy
    private UserService spyUserServices;

    @BeforeEach
    public void init(){
        MockitoAnnotations.openMocks(this);
    }

    @Test
    public void test1(){
        System.out.println("Mockito.mockingDetails(mockUserService).isMock():"+Mockito.mockingDetails(mockUserService).isMock());
        System.out.println("Mockito.mockingDetails(mockUserService).isSpy():"+Mockito.mockingDetails(mockUserService).isSpy());
        System.out.println("Mockito.mockingDetails(spyUserServiceS).isMock():"+Mockito.mockingDetails(spyUserServices).isMock());
        // spy对象是另一种类型的mock对象
        System.out.println("Mockito.mockingDetails(spyUserServiceS).isSpy():"+Mockito.mockingDetails(spyUserServices).isSpy());
    }
}

2.mock和spy对象

mock对象:

  • 方法插桩:执行插桩逻辑
  • 方法不插桩:返回mock对象的默认值
  • 作用对象:类吗,接口

spy对象:

  • 方法插桩:执行插桩逻辑
  • 方法不插桩:调用真实方法
  • 作用对象:类吗,接口

3.参数匹配

@Test
public void test1() {
    UserDO userDO = new UserDO();
    userDO.setId(1L);
    doReturn("nothing").when(mockUserService).selectNameById(userDO.getId());
    // 只有执行Mockito.doReturn才执行插桩
    System.out.println(mockUserService.selectNameById(userDO.getId()));
    // userDO2对象不进行插桩,返回默认值
    UserDO userDO2 = new UserDO();
    userDO2.setId(2L);
    System.out.println(mockUserService.selectNameById(userDO2.getId()));
}
    /**
     * 此时我只想拦截UserDO类型的任意对象
     * ArgumentMatchers.*拦截任意类型
     */
    @Test
    public void test2() {
        doReturn("nothingName").when(mockUserService).selectNameById(ArgumentMatchers.anyLong());
        UserDO userDO = new UserDO();
        userDO.setId(1L);
        System.out.println(mockUserService.selectNameById(userDO.getId()));
        UserDO userDO2 = new UserDO();
        userDO2.setId(2L);
        System.out.println(mockUserService.selectNameById(userDO2.getId()));
    }

方法插桩

指定调用某个方法时的行为,达到相互隔离的效果

  • 返回指定值
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.when;

@ExtendWith(MockitoExtension.class)
public class StubTest {
    @Mock
    private List<String> mockList;
    /**
     * 指定返回值
     */
    @Test
    public void test1() {
        // 方法一
        doReturn("zero").when(mockList).get(0);
        // 如果返回值不相等,则测试不通过
        Assertions.assertEquals("zero", mockList.get(0));

        //方法二
        when(mockList.get(1)).thenReturn("one");
        Assertions.assertEquals("one", mockList.get(1));
    }
}
  • void返回值方法插桩
/**
  * void 返回值插桩
  */
@Test
public void test2() {
    // 调用mockList.clear()什么也没做
    doNothing().when(mockList).clear();
    mockList.clear();
    verify(mockList, times(1)).clear();
}
  • 插桩的两种形式

    • when(obj.someMethod.thenXXX):用于mock对象
    • doXXX():用于mock/spy对象

    区别在于不插桩时doXXX应用与spy对象时会调用真实方法

    public void test4() {
        when(mockUserServiceImpl.getNumber()).thenReturn(99);
        System.out.println("mockUserServiceImpl.getNumber():"+mockUserServiceImpl.getNumber());
        doReturn(99).when(spyUserServiceImpl).getNumber();
        System.out.println("spyUserServiceImpl.getNumber() = " + spyUserServiceImpl.getNumber());
    }
    
  • 抛异常

/**
     * 断言异常
     */
    @Test
    public void test3() {
        doThrow(RuntimeException.class).when(mockList).get(anyInt());
        try{
            mockList.get(10000);//此处get(10000)为空
            Assertions.fail();
        }catch (RuntimeException e){
            // 断言表达式为真
            Assertions.assertTrue(e instanceof RuntimeException);
        }
    }
  • 多次插桩
/**
     * 多次插桩
     */
    @Test
    public void test4() {
        // 第一次调用返回1,第二次返回2,第三次返回3以及以后得调用都返回三
        when(mockList.size()).thenReturn(1)
                .thenReturn(2)
                .thenReturn(3);
        Assertions.assertEquals(1,mockList.size());
        Assertions.assertEquals(2,mockList.size());
        Assertions.assertEquals(3,mockList.size());
        Assertions.assertEquals(4,mockList.size());
    }
  • thenAnswer
/**
     * thenAnswer:实现指定的插桩逻辑
     */
    @Test
    public void test5() {
        // 此处不管传入什么参数,都返回100*3
        when(mockList.get(anyInt())).thenAnswer(new Answer<String>() {
            /**
             * 泛型是要返回插桩的返回值类型
             * @param invocationOnMock
             * @return
             * @throws Throwable
             */
            @Override
            public String answer(InvocationOnMock invocationOnMock) throws Throwable {
                Integer argument = invocationOnMock.getArgument(0, Integer.class);
                return String.valueOf(argument*100);
            }
        });
        String s = mockList.get(3);
        Assertions.assertEquals("300",s);
    }
  • 执行真正的原始方法
/**
     * 执行真正的原始方法
     */
    @Test
    public void test6() {
        when(mockUserServiceImpl.getNumber()).thenCallRealMethod();
        int number = mockUserServiceImpl.getNumber();
        Assertions.assertEquals(1,number);
    }
  • verify的使用
    /**
     * 指定方法返回几次
     */
    @Test
    public void test3() {
        mockUserService.add("lily", "123123", new ArrayList<>());
        // 验证add方法被调用了1次
        Mockito.verify(mockUserService, Mockito.times(1))
                .add("lily", "123123", new ArrayList<>());
        // 要么全部使用ArgumentMatchers,不能一半使用参数,一半使用ArgumentMatchers
        Mockito.verify(mockUserService, Mockito.times(1))
                .add(ArgumentMatchers.anyString(), ArgumentMatchers.anyString(), ArgumentMatchers.anyList());
    }

4.@InjectMocks注解

  • 作用:如果@InjectMocks声明的变量需要用到mock,spy对象,mockito会自动使用当前类里的mock或者spy成员进行an类型或者名字注入
  • 原理:构造器注入,setter注入,字段反射注入
@ExtendWith(MockitoExtension.class)
public class InjectMocksTest {

    /**
     * 被InjectMocks标注的属性,必须是实现类,因为Mockito会创建InjectMocks注解的类的实例,
     * 并注入到被InjectMocks标注的属性中,默认创建的对象就是没有经过Mockito处理过的对象,因此
     * 配合@Spy注解,变成可以调用默认方法的mock对象
     */
    @InjectMocks
    @Spy
    private UserServiceImpl userService;

    @Mock
    private UserFeatureService userFeatureService;

    @Test
    public void test() {
        int number = userService.getNumber();
        Assertions.assertEquals(1, number);
    }
}

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

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

相关文章

网络安全详解

目录 引言 一、网络安全概述 1.1 什么是网络安全 1.2 网络安全的重要性 二、网络安全面临的威胁 2.1 恶意软件&#xff08;Malware&#xff09; 2.2 网络钓鱼&#xff08;Phishing&#xff09; 2.3 中间人攻击&#xff08;Man-in-the-Middle Attack&#xff09; 2.4 拒…

让C#程序在linux环境运行

今晚花一些时间&#xff0c;总结net程序如何在linux环境运行的一些技术路线。 1、采用.Net Core框架 NET Core 使用了 .NET Core Runtime&#xff0c;它可以在 Windows、Linux 和 macOS 等多个操作系统上运行。可以采用Visual Studio生成Linux版本的dll。 在Linux系统中&…

救生圈检测系统源码分享

救生圈检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Visio…

Python基础学习(3)

目录 一&#xff0c;函数 1&#xff0c;函数的定义 2&#xff0c;函数的参数 1&#xff0c;默认值 2&#xff0c;传参 3&#xff0c;返回值 4&#xff0c;变量的作用域 5&#xff0c;函数的调用 二&#xff0c;常用数据结构 1&#xff0c;列表 列表的定义 列表的特性…

机器学习的应用领域

机器学习在许多领域有广泛的应用&#xff0c;下面列出了一些主要的应用领域及其典型应用&#xff1a; 1. 图像识别 人脸识别&#xff1a;用于解锁手机、自动标记照片、监控安全系统。物体识别&#xff1a;应用于自动驾驶汽车、机器人、医疗影像分析中&#xff0c;帮助机器理解…

vue3 TagInput 实现

效果 要实现类似于下面这种效果 大致原理 其实是很简单的,我们可以利用 element-plus 组件库里的 el-tag 组件来实现 这里我们可以将其抽离成一个公共的组件,那么现在有一个问题就是通讯问题 这里我们可以利用父子组件之间的通讯,利用 v-model 来实现,父组件传值,子组…

蓝桥杯15届C/C++B组省赛题目

问题描述 小蓝组织了一场算法交流会议&#xff0c;总共有 5050 人参加了本次会议。在会议上&#xff0c;大家进行了握手交流。按照惯例他们每个人都要与除自己以外的其他所有人进行一次握手 (且仅有一次)。但有 77 个人&#xff0c;这 77 人彼此之间没有进行握手 (但这 77 人与…

Unity数据持久化4——2进制

概述 基础知识 各类型数据转字节数据 文件操作相关 文件相关 文件流相关 文件夹相关 练习题 using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Text; using UnityEngine;public class Exercises1 : MonoBehaviour {/…

金融科技与银行业的数字化转型

随着科技的迅猛发展&#xff0c;金融科技已经成为推动银行业数字化转型的重要力量。从移动支付到区块链&#xff0c;再到人工智能&#xff0c;这些新兴技术正逐渐改变银行的运作方式&#xff0c;不断提高银行的服务效率、提升客户体验&#xff0c;并推动整个金融生态系统的变革…

大数据-143 - ClickHouse 集群 SQL 超详细实践记录!

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 目前已经更新到了&#xff1a; Hadoop&#xff08;已更完&#xff09;HDFS&#xff08;已更完&#xff09;MapReduce&#xff08;已更完&am…

代码编辑器 —— Notepad++ 实用技巧

目 录 NotePad常用技巧一、查找二、标记三、插件四、自动补全 NotePad常用技巧 Notepad 的吉祥物是一只变色龙。它广泛应用于编程、网页开发、文本处理、脚本编写、文档编辑等领域。 一起看看它有哪些功能和特点&#xff1a; 1、对众多编程语言提供语法高亮显示 2、可折叠函数…

redis简单使用与安装

redis redis 是什么 Redis 是一个开源的&#xff0c;使用 C 语言编写的,支持网络交互的,内存中的Key-Value 数据结构存储系统&#xff0c;支持多种语言,它可以用作数据库、缓存和消息中间件。 一、存储系统特性 内存存储与持久化 Redis 主要将数据存储在内存中&#xff0c;这…

某省公共资源交易中心爬虫逆向分析

目标网站 aHR0cHM6Ly95Z3AuZ2R6d2Z3Lmdvdi5jbi8jLzQ0L3NjenQteHEvP3VzZXJJZD02NzM4OTg2MzkyNjA3NzAzMDQmcm93SWQ9NTI1MDYyMDI2ODg0NzE2NTQ0JnRpbWU9MjAwOC0xMS0yNiZjZXJ0aWZpY2F0ZU5vPTkxNDQwOTA0NjgyNDI2MzU4QyZjZXJ0aWZpY2F0ZVR5cGU9Mjg 一、抓包分析 请求头参数加密 二、…

【C语言-数据结构】单链表的定义

单链表的定义&#xff08;实现&#xff09; 比较顺序表和单链表的物理存储结构就能够清楚地发现二者的区别 用代码定义一个单链表 typedef struct LNode{ElemType data; //每个结点存放一个数据元素struct LNode* next; //指针指向下一个结点 }LNode, *LinkList;//要表示一个…

微信CRM系统适合什么企业?

CRM&#xff08;客户关系管理&#xff09;系统适合多种行业和企业&#xff0c;包括但不限于&#xff1a;传统制造业、互联网行业、电商行业、医疗行业、教育行业、交通运输行业、汽车行业、房地产行业、金融行业、银行 CRM的功能覆盖了与客户接触的各个阶段&#xff0c;包括售…

python --PyAibote自动化

官文: https://www.pyaibote.com/ 下载安卓集成环境: 可以看到开发的一些信息

【AI视频】AI虚拟主播制作网站推荐

一、什么是AI虚拟主播&#xff1f; AI虚拟主播是一种利用人工智能技术打造的虚拟主持人&#xff0c;也被称为数字虚拟主持人。它们通常是由人工智能技术和三维建模技术结合而成&#xff0c;可以在各种平台上进行主持工作&#xff0c;如新闻报道、电商直播、综艺娱乐等。 AI虚…

华润电力最新校招社招润择认知能力测评:逻辑推理数字计算语言理解高分攻略

​ 尊敬的求职者们&#xff0c; 在您准备加入华润电力这个大家庭之前&#xff0c;了解其招聘测评的详细流程和要求是至关重要的。以下是我们为您整理的测评系统核心内容&#xff0c;希望对您的求职之旅有所帮助。 测评系统概览 华润电力的招聘测评系统旨在全面评估求职者的认…

【全网最全】2024年华为杯研赛B题成品论文获取入口(后续会更新)

您的点赞收藏是我继续更新的最大动力&#xff01; 一定要点击如下的卡片&#xff0c;那是获取资料的入口&#xff01; 点击链接加入【2024华为杯研赛资料汇总】&#xff1a;https://qm.qq.com/q/hMgWngXvcQhttps://qm.qq.com/q/hMgWngXvcQ你是否在寻找数学建模比赛的突破点&a…

二叉树(二)深度遍历和广度遍历

一、层序遍历 广度优先搜索&#xff1a;使用队列&#xff0c;先进先出 模板&#xff1a; 1、定义返回的result和用于辅助的队列 2、队列初始化&#xff1a; root非空时进队 3、遍历整个队列&#xff1a;大循环while(!que.empty()) 记录每层的size以及装每层结果的变量&a…