Mockito单元测试Mockito对Service层的测试案例

news2024/12/22 19:17:06

前言

以下是关于Mockito的API使用文档

官网:http://mockito.org/
官网英文API文档:https://javadoc.io/static/org.mockito/mockito-core/5.10.0/help-doc.html#index
非官方中文API文档:https://gitee.com/wnboy/mockito-doc-zh#mockito-%E4%B8%AD%E6%96%87%E6%96%87%E6%A1%A3–2026-beta-

单元测试

在这里插入图片描述单元测试(Unit Testing)是软件开发过程中的一种基本测试方法,它针对程序中的最小可独立测试的单元进行验证。这个“单元”通常是指代码中最基本的模块化结构,如函数、方法或类。在面向对象编程中,一个单元可以是一个单一的方法;在过程式编程中,它可以是一个单独的函数。

在这里插入图片描述在单元测试中,我们要保证我们的测试环境是独立的,不需要借助于数据库、外部文件等,我们关心的就只有实际运行代码的逻辑是否正确。所以单元测试更多我个人认为侧重点应该是代码层面。

在这里插入图片描述那么问题来了,单元测试既然需要独立不依赖于外部数据,那么在实际开发中,对于Service层来说,大部分都是得触及数据库,那么对于这种情况该怎么解决问题呢???

在这里插入图片描述答案很明显,就是本期要介绍的Mockito测试框架


Mockito基本介绍

在这里插入图片描述Mockito是一个流行的Java单元测试框架,专门用于模拟(mocking)和 stubbing(预设行为)。在软件开发的测试阶段,Mockito允许开发者创建并配置模拟对象来替代真实依赖项,这样可以在隔离的环境中对特定代码片段进行单元测试,而无需关心那些依赖对象的具体实现或外部服务的状态。

在这里插入图片描述所以在单元测试中,对于Service层的触及数据库的那一个部分,我们可以使用Mockito提供的工具,将那些访问数据库甚至说模仿请求的部分方法或者代码给模拟欺骗过去!!!也就说,Mocktio可以帮我们假装访问过数据库等需要外部资源的操作,而不需要真实去触及那部分数据

在这里插入图片描述所以想要做好单元测试只会使用一个Junit是不够的,在成为测试之神的路上还得不断学习… Anyway,我们继续往下看…


Mockito必要配置

依赖导入

使用Mockito需要导入相关依赖

<dependency>
     <groupId>org.mockito</groupId>
     <artifactId>mockito-core</artifactId>
     <scope>test</scope>
</dependency>

必要注解

我们需要在测试类上添加下面三个注解
@SpringBootTest
@RunWith(MockitoJUnitRunner.class)
@TestExecutionListeners(listeners = MockitoTestExecutionListener.class)

@SpringBootTest
注解用于标记测试类,表示该类是一个Spring Boot应用程序的测试类。
@RunWith(MockitoJUnitRunner.class)
注解用于指定测试运行器,使用Mockito框架的JUnit运行器。
@TestExecutionListeners(listeners = MockitoTestExecutionListener.class)
注解用于指定测试执行监听器,使用Mockito框架的测试执行监听器。


Mockito构造虚拟对象

在这里插入图片描述因为Mockito是一个模拟工具,所以我们在单元测试中,如果是触及外部资源的方法类,我们最好是通过Mockito去构造出对应的虚拟对象,这样我们的Mockito才有更多的操作手段。

而构造这样一个虚拟对象的方法如下:

  1. mock(Class classToMock))方法
  2. 注解
    • @Mock注解
    • @Spy注解
    • @InjectMocks注解(主要是为了打桩,后面介绍)

mock(Class classToMock)

我们使用方法构造时,只需要将class对象传入方法中即可,如下

Fans fans = mock(Fans.class);

注解(@InjectMocks、@Mock、@Spy)

注解类的方式也很简单,就类似我们使用@Autowired一样,在对象属性上添加注解就好了

@InjectMocks
private FansServiceImpl fansService;

@Mock
private FansMapper fansMapper;

@spy
private Fans fans;

在Mockito框架中,@InjectMocks@Mock@Spy 是用于创建模拟对象(mocks)和部分真实行为的对象(spies)的注解,并将它们注入到被测试类中的依赖关系中。下面是这三个注解的区别:

  1. @Mock

    • 使用此注解来创建一个模拟对象。当用此注解标记一个字段时,Mockito 会为该类型创建一个 mock 实例。
    • 模拟对象不执行任何实际的方法实现,而是允许你定义它们的行为(即方法调用时返回什么值或者抛出什么异常等)。
    • 对于 mock 对象,除非你明确配置了行为,否则所有方法默认都会返回默认值或空集合。
  2. @Spy

    • 使用 @Spy 注解可以创建一个“间谍”对象,它本质上是一个部分真实的对象。
    • spy 是原始对象的一个包装,它默认情况下会调用真实对象的方法,但在必要时也可以对某些方法进行模拟(stubbing)。
    • 这意味着当你想要跟踪或修改某个特定方法的行为时,同时保持其他方法的真实功能,就可以使用 spy。
  3. @InjectMocks

    • 当你有一个复杂的类结构,需要在一个类中注入多个依赖项(可能是 mock 或 spy)时,使用 @InjectMocks 注解。
    • 此注解标注在你想实例化的被测试类上,Mockito 将自动创建这个类的实例,并尝试通过构造函数或 setter 方法将所有带有 @Mock@Spy 注解的字段注入到这个实例中。
    • 目的是为了构建一个具有部分或全部模拟依赖项的被测对象,从而可以在单元测试中只关注被测类本身的逻辑。

总结来说,在 Mockito 测试场景中,通常会结合使用这些注解:

  • 如果你需要完全控制一个依赖对象的行为,就使用 @Mock 创建纯模拟对象。
  • 如果希望大部分时候依赖对象表现其真实行为,仅针对某些情况模拟,那么使用 @Spy
  • @InjectMocks 则用于装配整个被测试对象及其依赖关系,使得在测试过程中能够专注于被测试类的功能验证,而不必手动管理各个依赖项的注入。

Mockito的一些简单方法

方法预览图
在这里插入图片描述

when

when() 方法主要是用来捕捉虚拟对象的动作,当虚拟对象做出指定动作时,触发前置或者后置任务。

when有两种比较常用的操作,分别是doReturn().when()when().thenReturn()

doReturn().when()when().thenReturn() 在 Mockito 中都用于预设模拟对象(mock)的方法调用返回值,但它们在某些特定场景下有微妙的区别:

  1. when().thenReturn()

    • 这是Mockito中最常见的 stubbing 方法。
    • 它直接设置指定方法调用时的返回值。
    • 当使用 when().thenReturn() 时,如果该方法已经被调用过,则不会应用新的存根规则。也就是说,它仅对后续的调用生效。(也就是说,这个规则只要你不修改,后续都是这个返回结果,但是你后续修改了规则,也不会影响你之前调用产生的结果。

    示例:

    List ist = Mockito.mock(List.class);
    when(ist.get(0)).thenReturn("first"); // 设置 get(0) 的返回值为 "first"
    
  2. doReturn().when()

    • doReturn() 是一种更底层、更灵活的stubbing方式,它允许你在任何情况下改变方法的返回值,无论方法是否已经被调用过。
    • 使用 doReturn().when() 可以多次stub同一个方法,并且不论之前该方法是否被调用过,都会立即生效。
    • 特别地,当与条件匹配器结合使用时,doReturn().when() 更强大,因为它可以定义基于不同条件的不同返回行为。

    示例:

    List list = Mockito.mock(List.class);
    doReturn("first").when(list).get(0); // 即使 get(0) 已经被调用过,也会立即生效
    
    // 或者带有条件的 stubbing
    doReturn("value if empty").when(list).get(anyInt()).isNull(); // 如果 get 的参数为空,则返回 "value if empty"
    

总结起来,在大多数情况下,二者都可以用来设置方法调用的返回值。然而,doReturn().when() 提供了更多的灵活性,特别是在需要多次或条件性地改变一个方法的返回值时。而 when().thenReturn() 更加直观简洁,适用于大部分简单的存根需求。

any

在Mockito框架中,any() 方法是一个非常常用的参数匹配器(Argument Matcher),它允许我们在验证方法调用或者设置方法行为时对传入的参数进行灵活匹配。当使用 any() 时,Mockito并不会检查具体的参数值,而是表示“任何”类型的参数都可接受。

例如:

// 设置mock对象的行为,anyInt=>不关心具体传入的int参数是多少
when(list.get(Mockito.anyInt())).thenReturn("A mock value");

// 验证方法调用,anyString=>不关心具体传入的String参数是什么
verify(list).add(Mockito.anyString());

在上述例子中,

  • list.get(Mockito.anyInt()) 表示对于mockedList对象的get方法,不论传入的是任意整数值,都将返回预设的"mock value"。
  • Mockito.verify(list).add(Mockito.anyString()) 则是验证list对象是否在其上调用了add方法,并且传入了一个字符串参数,而不在乎这个字符串参数的具体内容是什么。

从Java 8开始,由于类型推断的改进,可以直接使用无参数版本的 any() 方法,编译器会根据上下文自动推断出需要匹配的参数类型:

Mockito.when(list.get(anyInt())).thenReturn("A mock value");
Mockito.verify(list).add(anyString());

除了 any(),Mockito还提供了针对不同类型的 anyXxx() 方法,如 anyString(), anyList(), any(Class<T>) 等,它们都是用来做相应类型的参数匹配。

verify

在Mockito框架中,verify() 是一个非常关键的方法,用于验证模拟对象(mocks)是否按照预期的方式被调用了。通过 verify() 方法,你可以检查在测试执行过程中,某个模拟对象的特定方法是否被调用,以及调用的具体次数和参数。

例如:

// 创建并配置一个模拟对象
List list = Mockito.mock(List.class);
list.add("one");

// 验证add方法是否被调用了一次,并且传入的是"one"
Mockito.verify(list).add("one");

// 验证add方法是否被调用了任意次数,并且传入的是任何字符串
Mockito.verify(list).add(Mockito.anyString());

// 验证add方法是否被调用了两次
Mockito.verify(list, times(2)).add(Mockito.any());

在上述示例中,

  • Mockito.verify(list).add("one") 检查 list 是否被调用了 add("one")
  • Mockito.verify(list, times(2)).add(Mockito.any()) 检查 listadd 方法是否被调用了两次,无论添加什么参数。

使用 verify() 方法有助于确保你的代码按预期与依赖项交互,从而提高测试覆盖率和代码质量。

atxxx

这些方法是Mockito框架中的验证调用次数的工具,它们与 verify() 方法结合使用来检查模拟对象(mock)的方法是否按照预期的次数被调用。

  1. atLeast()

    Mockito.verify(mockedList, atLeast(2)).clear();
    

    这行代码会验证 mockedList.clear() 方法至少被调用了两次。如果实际调用次数少于2次,验证将失败。

  2. atLeastOnce()

    Mockito.verify(list, atLeastOnce()).clear();
    

    这个简写形式表示至少调用一次。它等同于 atLeast(1),用于验证 list.clear() 方法至少被调用了一次。

  3. atMost(1)

    Mockito.verify(list, atMost(1)).clear();
    

    这行代码验证 list.clear() 方法最多被调用了一次。也就是说,调用0次或1次都是满足条件的,但超过1次则验证失败。

  4. atLeast(1)

    Mockito.verify(list, atLeast(1)).clear();
    

    这行代码表示 list.clear() 方法至少需要被调用一次。这和 atLeastOnce() 是等价的,都是要求目标方法至少执行一次。

通过这些方法,你可以更精确地控制并验证测试过程中对模拟对象方法的调用次数,从而确保代码按预期进行交互。


对Service层的单元测试实操案例

回到,我们这篇文章最初的一个问题,如果要对Service层中涉及数据库的方法调用该怎么做,我们直接上实操代码,如下:

相关代码
dto层Fans类

public class Fans implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.ASSIGN_UUID)
    private String uuid;

    private long uid;

    private long fid;

    public Fans(long uid, long fid) {
        this.uid = uid;
        this.fid = fid;
    }
}

Service层

@Service
public class FansServiceImpl extends ServiceImpl<FansMapper, Fans> implements IFansService {

    @Autowired
    private FansMapper fansMapper;

    @Override
    public boolean addFans(Fans fans) {
        String uuid = UUID.randomUUID().toString();
        fans.setUuid(uuid);
        return fansMapper.insert(fans) == 1 ;
    }
}

test类

class TestFans {
    // FansServiceImpl对象,用于测试
    @InjectMocks
    private FansServiceImpl fansService;
    // FansMapper对象,用于模拟
    @Mock
    private FansMapper fansMapper;

    @Test
    public void testAddFans() {
        // 创建Fans对象
        Fans fans = new Fans(6,62L);
        // 模拟FansMapper的insert方法,返回1
        when(fansMapper.insert(any(Fans.class))).thenReturn(1);
        // 使用doReturn方法模拟FansMapper的insert方法,返回1
        // doReturn(1).when(fansMapper.insert(any(Fans.class)));

        // 调用fansService的addFans方法
        boolean result = fansService.addFans(fans);
        // 断言结果为true
        Assertions.assertTrue(result);
        // 验证fansMapper的insert方法被调用了一次
        verify(fansMapper, times(1)).insert(any(Fans.class));
    }
}

在这里插入图片描述
在这段代码中,需要做出点解释,对于fansService来说,是我们要测试的对象,所以我们需要用@InjectMocks来进行注释,说明这个是我们的目标测试对象,也就是“打桩”,而fansMapper则使用@Mock来注释是因为,fansMapper是fansService对象的一个属性,@InjectMock会自己去找被@Mock所注释的并且合适的虚拟对象进行依赖注入(类似于@Component中有的对象属性使用了@Autowired


END
声明:本文章为个人学习笔记,希望能对你有帮助,后续也会慢慢更新有关Mockito相关的文章,感谢阅读!!!

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

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

相关文章

c语言字符函数和字符串函数

目录 1. 字符分类函数2. 字符转换函数3. strlen的使用和模拟实现4. strcpy的使用和模拟实现5. strcat的使用和模拟实现6. strcmp的使用和模拟实现7. strncpy函数的使用8. strncat函数的使用9. strncmp函数的使用10. strstr的使用和模拟实现11. strtok函数的使用12. strerror函数…

阿里云的流量价格表_2024阿里云服务器流量费用表

阿里云服务器宽带按使用流量怎么收费的&#xff1f;价格为0.8元/GB&#xff0c;地域不同流量价格也不同&#xff0c;北京、杭州、上海、深圳等中国大陆地域是0.8元每GB&#xff0c;中国香港是1元/GB&#xff0c;美国流量0.5元1GB、日本流量0.6元、韩国流量0.8元&#xff0c;阿里…

利用Dynamo为家具族三维截图并导入到明细表

前几天我在朋友圈发了一个小视频&#xff0c;是利用Dynamo为家具族截图&#xff0c;并将截图添加到族参数&#xff0c;以便于在图纸中显示族的样子。效果如下&#xff1a; 此处为语雀视频卡片&#xff0c;点击链接查看&#xff1a; 利用Dynamo为家具族三维截图并导入到明细表 …

2.WEB渗透测试-前置基础知识-web基础知识和操作系统

web基础知识 1.http协议 超文本传输协议是互联网上应用最广泛的一种网络协议。所有www文件都必须遵守的一个标准&#xff0c;是以 ASCII 码传输&#xff0c;建立在 TCP/IP 协议之上的应用层规范&#xff0c;通俗点说就是一种固定的通讯规则。 2、网络的三种架构及特点 网络应…

港科夜闻|香港科大计划建立北部都会区卫星校园完善科大创新带,发展未来创新科技 未来医药发展及跨学科教育...

关注并星标 每周阅读港科夜闻 建立新视野 开启新思维 1、香港科大计划建立北部都会区卫星校园完善“科大创新带”&#xff0c;发展未来创新科技、未来医药发展及跨学科教育。香港科大校长叶玉如教授在2月22日的媒体会议上表示&#xff0c;香港科大将在北部都会区建立卫星校园&a…

5.2.鸿蒙LiteOS-M los_dispatch

目录 一、cortex-m4 los_dispatch.S代码分析坚持就有收获 一、cortex-m4 los_dispatch.S代码分析 .syntax unified #.syntax [unified | divided], 指定arm 汇编语法规则 .arch armv7e-m #指定平台, 与命令行参数-march同样的作用 .fpu fpv4-sp-d16 #指定浮点运算…

Github 2024-02-21 开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2024-02-21统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目8非开发语言项目1TypeScript项目1 gpt4free 语言模型集合改进计划 创建周期&#xff1a;300 天开…

说说设备像素、css像素、设备独立像素、dpr、ppi 之间的区别

文章目录 一、背景二、介绍CSS像素设备像素设备独立像素dprppi 三、总结参考文献 一、背景 在css中我们通常使用px作为单位&#xff0c;在PC浏览器中css的1个像素都是对应着电脑屏幕的1个物理像素 这会造成一种错觉&#xff0c;我们会认为css中的像素就是设备的物理像素 但实…

Matlab/simulink基于vsg的风光储调频系统建模仿真(持续更新)

​ 1.Matlab/simulink基于vsg的风光储调频系统建模仿真&#xff08;持续更新&#xff09;

vue从flask获取数据并显示

记录一个前后端分离遇到的问题&#xff0c;即vue前端从flask后端获取数据。具体描述如下&#xff1a;flask只负责连接数据库并获取数据库的数据&#xff0c;并返回给前端vue&#xff1b;vue则需要获取后端返回的数据并显示。 方法如下&#xff0c;分别用一个vue组件和一个flas…

深入探究node搭建socket服务器

自从上篇中sokect实现了视频通话&#xff0c;但是是使用ws依赖库实现的服务端&#xff0c;所以最近再看ws源码&#xff0c;不看不知道&#xff0c;一看很惊讶。 接下来一点点记录一下&#xff0c;如何搭建一个简易的服务端socket&#xff0c;来实现上次的视频通讯。 搭建一个…

修复Microsoft Edge WebView2无法安装的问题

修复Microsoft Edge WebView2无法安装的问题 场景解决方案 场景 系统&#xff1a;win11 电脑&#xff1a;联想14 前提&#xff1a;使用Geek Uninstaller强制删除了Microsoft Edge WebView2 同时下载了clash verge。 发现根本无法运行&#xff08;点击了无任何反应且图标颜色…

C++面试题精选与解析

C面试题精选与解析 一、基础与语法 请问C中的指针和引用有什么区别&#xff1f; 指针是一个变量&#xff0c;存储的是另一个变量的内存地址。指针可以被重新赋值以指向另一个不同的对象。而引用是某个变量的别名&#xff0c;一旦引用被初始化为一个变量&#xff0c;就不能改变…

第四篇:CamX确认当前选择的usecase、pipeline、sensormode

第四篇:CamX确认当前选择的usecase、pipeline,sensormode 一、 当前UseCase logcat |grep “usecase selected” I/CHIUSECASE( 661): [CONFIG ] chxusecaseutils.cpp:867 GetMatchingUsecase() ZSL usecase selected二、当前pipeline logcat |grep “Selected sensor M…

python_ACM模式《剑指offer刷题》二叉树3

题目&#xff1a; 面试tips&#xff1a; 若面试官无特殊要求直接优先采用思路一递归法&#xff08;易想&#xff09;&#xff1b;若有特殊要求&#xff0c;例如不想要重复遍历中序序列来寻找根节点&#xff0c;则采取思路二&#xff0c;即将中序遍历存入到哈希表中&#xff0c;…

基于卷积神经网络的图像去噪

目录 背影 卷积神经网络CNN的原理 卷积神经网络CNN的定义 卷积神经网络CNN的神经元 卷积神经网络CNN的激活函数 卷积神经网络CNN的传递函数 基于卷积神经网络的图像去噪 完整代码:基于卷积神经网络的图像去噪.rar资源-CSDN文库 https://download.csdn.net/download/abc9918351…

《隐私计算简易速速上手小册》第2章:关键技术介绍(2024 最新版)

文章目录 2.1 同态加密2.1.1 基础知识2.1.2 主要案例:云计算数据分析2.1.3 拓展案例 1:医疗数据分析2.1.4 拓展案例 2:金融风险评估2.2 安全多方计算(SMC)2.2.1 基础知识2.2.2 主要案例:跨机构金融数据共享2.2.3 拓展案例 1:医疗研究合作2.2.4 拓展案例 2:跨国界数据交…

飞天使-linux操作的一些技巧与知识点7-devops

文章目录 简述devopsCICD 简述devops 让技术团队&#xff0c;运维&#xff0c;测试等团队实现一体式流程自动化 进阶版图 CICD 持续集成&#xff0c; 从编译&#xff0c;测试&#xff0c;发布的完成自动化流程 持续交付&#xff0c;包含持续集成&#xff0c;并且将项目部署…

复旦大学MBA:AIGC时代,科技与商业迸发更绚烂的火花

ChatGPT问世以来&#xff0c;AI技术及应用进入一个全速推进的通道&#xff0c;快速迈入通用大模型时代。从AGI(人工通用智能&#xff09;到AIGC(AI多模态内容生成&#xff09;&#xff0c;AI正在飞速重塑各个行业、人类生活乃至人类的未来。在商业领域更是给营销场景和营销工具…

Flutter开发进阶之Package

Flutter开发进阶之Package 通常我们在Flutter开发中需要将部分功能与整体项目隔离&#xff0c;一般有两种方案Plugin和Package&#xff0c;Application是作为主体项目&#xff0c;Module是作为原生项目接入Flutter模块。 当独立模块不需要与原生项目通讯只需要Plugin就可以&a…