mock详细教程入门这一篇就够了

2023-10-29

1、什么是mock测试

1.png

Mock测试就是在测试活动中,对于某些不容易构造或者不容易获取的比较复杂的数据/场景,用一个虚拟的对象(Mock对象)来创建用于测试的测试方法。

2、为什么要进行Mock测试

Mock是为了解决不同的单元之间由于耦合而难于开发、测试的问题。所以,Mock既能出现在单元测试中,也会出现在集成测试、系统测试过程中。

Mock最大的功能是帮你把单元测试的耦合分解开,如果你的代码对另一个类或者接口有依赖,它能够帮你模拟这些依赖,并帮你验证所调用的依赖的行为。

3、Mock适用场景

1、需要将当前被测单元和其依赖模块独立开来,构造一个独立的测试环境,不关注被测单元的依赖对象,只关注被测单元的功能逻辑。

2、被测单元依赖的模块尚未开发完成,而被测单元需要依赖模块的返回值进行后续处理。

3、前后端项目中,后端接口开发完成之前,接口联调

4、依赖的上游项目的接口尚未开发完成,需要接口联调测试

5、被测单元依赖的对象较难模拟或者构造比较复杂

如: 支付业务的异常条件很多,但是模拟这种异常条件很复杂或者无法模拟.

4、代码实例

新建测试工程

package com.echo.mockito;
 
public class demo {
 
    //新建一个测试方法
    public int add(int a,  int b){
        return a + b;
    }
}

构建mock测试方法

选中测试类,右键选中generate

2.png

点击test

3.png

点击ok后就会在test目录下生成对应的测试方法,和真实的目录是对应的

4.png

5、参数方法说明

@BeforeEach

用在测试前准备,测试前会构建很多的环境配置或者基础配置,都可以在这里设置。

@AfterEach

用于测试后设置。

@Mock

注解可以理解为对 mock 方法的一个替代,不会走真实的方法,模拟真实方法的行为。使用该注解时,要使用MockitoAnnotations.openMocks(this)  方法,让注解生效。

@Spy

1、被Spy的对象会走真实的方法,而mock对象不会,

2、spy方法的参数是对象实例,mock的参数是class。

@InjectMocks

用于将@Mock标记的模拟变量注入到测试类中。

MockitoAnnotations.openMocks(this)

开启mock,配合以上两个注解进行测试。一般放在@BeforeEach 中,在测试前开启,这样不用在每个方法中都的开启了。

Mockito.when(demo.add(1,2)).thenReturn(3):打桩

mock核心,可以设置要测试的方法的结果,这样就会忽略真实方法的执行结果,后续的测试都是基于打桩结果执行。

Mockito.when(demo.add(1,2)).thenThrow(new RuntimeException());

用于模拟异常。

Assertions.assertEquals(3,demo.add(1,2)):断言

测试的主要方式,结果基于此判断。(期望值,实际值)。

6、简单测试

打桩测试  设置方法返回4  ,而实际执行为3 ,测试不通过。

5.png

不打桩测试  因为是spy方式,会走真实的方法  ,测试通过。

6.png

如果是mock方式,如果不打桩会有默认值,测试会不通过,可以试一下。

7.png

7、测试方法说明

详细调用看代码,一般会以下几种:

  • 打桩测试

  • 异常测试

  • 真实方法调用

package com.echo.mockito;
 
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.Spy;
 
 
class demoTest {
 
     @Mock
     demo demo;
     //测试前开启mock
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }
 
    @Test
    void add() {
        //mock 打桩,就是不管真实的方法如何执行,我们可以自行假设该方法执行的结果
        //后续的测试都是基于打桩结果来走
       // Mockito.when(demo.add(1,2)).thenReturn(4);
       // Assertions.assertEquals(3,demo.add(1,2));
        //当测试方法出现异常,测试方法  如果有try{}catch{} 则可以测试异常是否正常
        //Mockito.when(demo.add(1,1)).thenThrow(new RuntimeException());
        //调用真实的方法
        Mockito.when(demo.add(1,1)).thenCallRealMethod();
        Assertions.assertEquals(2,demo.add(1,1));
    }
 
    @AfterEach
    void after(){
        System.out.println("测试结束");
    }
}

8、Mock静态方法

之前的版本中是不允许模拟测试静态方法的,如果需要测试静态方法,需要替换新的mock依赖,注释掉目前有的依赖,会有冲突。

静态方法测试要用mock的MockedStatic类构建测试方法。

  <!--   <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-core</artifactId>
            <version>4.6.1</version>
        </dependency>
-->
        <dependency>
            <groupId>org.mockito</groupId>
            <artifactId>mockito-inline</artifactId>
            <version>4.3.1</version>
            <scope>test</scope>
        </dependency>
package com.echo.mockito.Util;
 
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
 
import java.util.Arrays;
 
import static org.junit.jupiter.api.Assertions.*;
 
class StaticUtilsTest {
 
    @BeforeEach
    void setUp() {
    }
 
    // 有参静态方法构建
    @Test
    void range() {
        MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);
        //打桩
        demo.when(()->StaticUtils.range(2,6)).thenReturn(Arrays.asList(10,11,12));
        Assertions.assertTrue(StaticUtils.range(2,6).contains(11));
    }
    // 无参静态方法构建
    @Test
    void name() {
        MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);
        //打桩
        demo.when(StaticUtils::name).thenReturn("dhmw");
        Assertions.assertEquals("dhmw",StaticUtils.name());
 
    }
}

问题:单个的方法执行是没有问题的,但是我们在类上全部执行的时候,发现报错。

提示static mocking is already registered in the current thread  To create a new mock, the existing static mock registration must be deregistered

意思就是说,每个方法需要有自己的static mock 对象,不允许公用。一起执行的时候,第一个方法占了对象,第二个方法就没有办法再占了。

8.png

解决:每个方法执行完毕后就直接关闭mock对象 demo.close()。相当于是单例的。用完后就的释放,下一个方法才能接着使用。

  @Test
    void range() {
        MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);
        //打桩
        demo.when(()->StaticUtils.range(2,6)).thenReturn(Arrays.asList(10,11,12));
        Assertions.assertTrue(StaticUtils.range(2,6).contains(11));
       //关闭
        demo.close();
    }
    // 无参静态方法构建
    @Test
    void name() {
        MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);
        //打桩
        demo.when(StaticUtils::name).thenReturn("dhmw");
        Assertions.assertEquals("dhmw",StaticUtils.name());
        //关闭
        demo.close();
    }

9、提升测试覆盖率

案例:数据统计系统,地推人员输入客户的姓名和手机号码,最后构建用户对象存入数据表。

业务代码如下:

package com.echo.mockito.service.impl;
 
import com.echo.mockito.dao.UserDao;
import com.echo.mockito.service.RegistrationService;
import com.echo.mockito.vo.User;
import org.springframework.beans.factory.annotation.Autowired;
 
import javax.xml.bind.ValidationException;
import java.sql.SQLException;
 
public class RegistrationServiceImpl implements RegistrationService {
     @Autowired
     UserDao userDao;
    @Override
    public User register(String name, String phone) throws Exception {
        if (name == null || name.length() == 0){
            throw new ValidationException("name 不能为空");
        }
        if (phone == null || phone.length() ==0 ){
            throw new ValidationException("phone 不能为空");
        }
        User user;
        try {
              user = userDao.save(name,phone);
        }catch (Exception e){
            throw  new Exception("SqlException thrown" + e.getMessage());
        }
        return user;
    }
}
package com.echo.mockito.dao;
 
import com.echo.mockito.vo.User;
 
public class UserDao {
 
    public User save(String name,String phnoe){
        User user = new User();
        user.setName(name);
        return user;
    }
}

生成相应的测试代码,此时有几个问题需要思考。

1、测试的类是RegistrationServiceImpl但是里面的userDao如何注入到测试类中?

2、因为需要测试覆盖度,需要考虑该测试类中总共有几种情况需要测试。

(1):两个if抛出了两个异常,总共是2种情况要测试。

(2):保存数据库分为正常保存和异常保存,总共2种情况测试。

综上所述,必须完成这四种情况的测试,才能覆盖所有代码。

相同的方法,我们生成测试用例.代码中有详细的说明,每一种情况都有测试用例。

package com.echo.mockito.service.impl;
 
import com.echo.mockito.dao.UserDao;
import com.echo.mockito.vo.User;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.*;
 
import javax.xml.bind.ValidationException;
 
import java.sql.SQLException;
 
import static org.junit.jupiter.api.Assertions.*;
 
class RegistrationServiceImplTest {
 
  @InjectMocks     //RegistrationServiceImpl 实例中注入@Mock标记的类,此处是注入userDao
    @Spy
    private RegistrationServiceImpl registrationService;
    @Mock
    private  UserDao userDao;
    @BeforeEach
    void setUp() {
        MockitoAnnotations.openMocks(this);
    }
 
    @Test
    void register() throws Exception {
        // ------------------  第一种 name  异常情况   测试   start ------------------------
        String name = null;
        String phone = "1234";
        try {
          registrationService.register(name,phone);
        }catch (Exception e){
        Assertions.assertTrue(e instanceof ValidationException);
        }
 
        // ------------------  name  异常情况   测试   end  ------------------------
 
        // ------------------  第二种 phone  异常情况   测试   start  ------------------------
        name = "111";
        phone = null;
      try {
        registrationService.register(name,phone);
      }catch (Exception e){
        Assertions.assertTrue(e instanceof ValidationException);
      }
        // ------------------  phone  异常情况   测试   start  ------------------------
 
 
        // ------------------  第三种 userDao.save   正常情况   测试   start  ------------------------
      name = "111";
      phone = "111";
        //正常保存测试  打桩  走真实的方法
        Mockito.when(userDao.save(name,phone)).thenCallRealMethod();
        User user =  registrationService.register(name,phone);
        Assertions.assertEquals("111",user.getName());
        // ------------------  userDao.save   正常情况   测试   end  ------------------------
 
        // ------------------   第四种 userDao.save   异常情况   测试   start  ------------------------
      //异常保存测试  打桩   通过thenThrow 抛出异常  测试异常是否被捕获
      Mockito.when(userDao.save(name,phone)).thenThrow(new RuntimeException());
      try {
         registrationService.register(name,phone);
      }catch (Exception e){
        Assertions.assertTrue(e instanceof Exception);
      }
// ------------------  userDao.save   异常情况   测试   end  ------------------------
    }
}

如上所示:上面提到的第一个问题,如何注入测试类中的成员变量,是通过@InjectMocks 注解即可完成。

测试覆盖率方法如下:

9.png

测试结果如下:

1、右边部分会显示测试覆盖率。

2、真实代码绿色代表已覆盖测试,红色代表未覆盖测试。

11.png

如果所有的测试情况都100%覆盖,结果如下:

12.png

综上所述就是覆盖测试的方法,总结如下:

1、根据业务代码,分析出所有需要测试的情况。

2、根据不同的测试情况,编写具体的测试代码。

3、针对每一种情况,可以编写具体的测试代码,然后通过打桩,断言等方式,穷尽所有的清册情况即可。

问题:

1、如果真实代码 方法级别有 throws Exception  那么同样的,测试方法也必须方法级别要抛出异常,不然测试会报错。

    @Test
    void register() throws Exception {

 2、保存数据库分为正常和异常,那么先测正常分支,然后在测试异常分支,如果顺序反了,测试先抛出异常,正常的分支就不会执行,这样会导致测试覆盖不全。


学习资源分享

最后感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走

这些资料,对于做【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!凡事要趁早,特别是技术行业,一定要提升技术功底。希望对大家有所帮助…….

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

mock详细教程入门这一篇就够了 的相关文章

随机推荐

  • 1、在VMware上如何创建虚拟机以及安装linux操作系统 2、修改主机名为server.local 3、将虚拟机的网络连接模式分别调换成仅主机模式、nat模式、桥接模式并获取ip地址使用ssh通过

    一 在VMware上如何创建虚拟机以及安装linux操作系统 创建虚拟机 1 点击创建新的虚拟机 2 点击自定义 点击下一步 3 兼容性确认好 点击下一步 4 选择稍后安装操作系统 点击下一步 5 选择linux 下一步 6 选择Linux
  • 佳能打印机复印身份证的双面到A4纸上的方法

    1 选择其他功能 页面合并 2 选择来源纸张大小 身份证可以选择A5R大小的 3 选择打印纸张大小 默认A4 4 完成设置 按那个平常的复印将进行扫描 画面上会显示继续扫面R2 然后将身份证翻转放到原来的地方 然后再按扫描 5 最后按界面上
  • centOS7 中安装可视化图形界面

    centOS7 中安装可视化图形界面 一 检查 yum是否可用 输入 yum list 如果出现以下情况说明不可用 二 下载可视化图形界面 2 1 安装 x Windows System 输入 yum groupinstall X Wind
  • html+写入数据库+JDBC更改数据库

    1 HTML部分 2 CSS部分 Login div div div div class input box div div
  • starter-canal 下载并打成依赖放入maven本地仓库

    https github com chenqian56131 spring boot starter canal 下载代码 1 下载完成进入项目根目录starter canal 中 命令行如下 mvn install 2 等待打包完成 ta
  • SpringBoot集成WebSocket实现客户端与服务端长连接通信

    场景 1 WebSocket协议是用于前后端长连接交互的技术 此技术多用于交互不断开的场景 特点是连接不间断 更轻量 只有在关闭浏览器窗口 或者关闭浏览器 或主动close 当前会话对象才会关闭 2 相较于 Http Https 通信只能由
  • python解释器怎么添加_Python解释器的配置

    1 准备工作 安装好Pycharm2017版本 电脑上安装好Python解释器 2 本地解释器配置 配置本地解释器的步骤相对简洁直观 1 单击工具栏中的设置按钮 2 在Settings Preferences对话框中选中 Project I
  • 人脸跟踪开源项目

    https github com xiangdeyizhang FaceTrack ncnn HyperFT https github com qaz734913414 Ncnn FaceTrack Ncnn FaceTrack 基于mtc
  • 数字水印简介

    转自 http baike baidu com view 39205 htm 数字水印 Digital Watermarking 技术是将一些标识信息 即数字水印 直接嵌入数字载体当中 包括多媒体 文档 软件等 或是间接表示 修改特定区域的
  • 经纬度坐标正则验证

    经纬度正则验证表达式 纬度正则表达式 90 至 90 0 8 d 1 d 0 6 90 0 0 6 0 8 d 1 90 经度正则表达式 180 至 180 d 1 9 d 1 0 7 d 0 1 3 d 0 6 d 1 9 d 1 0 7
  • 趣味解C——编程(1)

    前言 题目虽是基础题 旨在用趣味使其乐趣性翻倍 纯属娱乐 解题思路 1 房间 一共n间 我们可以设其为 i 从而定义循环 for i 1 i lt n i 2 服务员 各个服务员都记得自己的使命 ta们没有名字 只知道自己是第几 于是1号最
  • Unity 面试题汇总(三)Unity 基础相关

    Unity 面试题汇总 三 Unity 基础相关 目录 Unity 面试题汇总 三 Unity 基础相关 0 FSM 状态机 HFSM 分层状态机 BT 行为树 的区别 1 什么是协同程序 2 Unity3D中的碰撞器和触发器的区别 3 物
  • 反射概述

    package com kuang reflection 什么叫反射 public class test02 public static void main String args throws ClassNotFoundException
  • SA实战 ·《SpringCloud Alibaba实战》微服务介绍

    微服务化后的问题 一个系统 尤其是大型系统使用微服务架构模式进行搭建和开发时 虽然总体上能够提高研发效率 能够支持更高的并发 也能够提高系统整体的性能和可靠性 以及可维护性 但是在实现细节上还是存在着不少的问题 1 将系统拆分成各个微服务后
  • 御剑的使用

    御剑 1 扫描线程自定义 用户可根据自身电脑的配置来设置调节扫描线程 2 集合DIR扫描 ASP ASPX PHP JSP MDB数据库 包含所有网站脚本路径扫描 3 默认探测200 也就是扫描的网站真实存在的路径文件 我们使用御剑扫描器
  • Java基础8--Scanner类

    Java基础8 Scanner类 java util Scanner 是 Java5 的新特征 我们可以通过 Scanner 类来获取用户的输入 下面是创建 Scanner 对象的基本语法 Scanner s new Scanner Sys
  • 程序员必须知道的十大算法之 快速排序

    快速排序是由东尼 霍尔所发展的一种排序算法 在平均状况下 排序 n 个项目要 nlogn 次比较 在最坏状况下则需要 n2 次比较 但这种状况并不常见 事实上 快速排序通常明显比其他 nlogn 算法更快 因为它的内部循环 innerloo
  • 设计原则与思想:总结课

    文章目录 设计原则与思想 总结课 总结回顾面向对象 设计原则 编程规范 重构技巧等知识点 一 代码质量评判标准 如何评价代码质量的高低 最常用的评价标准有哪几个 如何才能写出高质量的代码 二 面向对象 三 设计原则 四 规范与重构 常见的
  • Win10防止Windows安全中心(Windows Defender)乱删文件

    Windows Defender乱删文件着实让人哭笑不得 还好它还有个排除项 不然只能安个杀毒软件接管它了 点击 开始 点击 设置 开始上面的齿轮图标 打开Windows设置 点击最下面的 更新和安全 点击 Windows安全中心 点击右侧
  • mock详细教程入门这一篇就够了

    1 什么是mock测试 1 png Mock测试就是在测试活动中 对于某些不容易构造或者不容易获取的比较复杂的数据 场景 用一个虚拟的对象 Mock对象 来创建用于测试的测试方法 2 为什么要进行Mock测试 Mock是为了解决不同的单元之