Mock测试详细教程入门这一篇就够了

2024-04-27 00:36

本文主要是介绍Mock测试详细教程入门这一篇就够了,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

 🔥 交流讨论:欢迎加入我们一起学习!

🔥 资源分享耗时200+小时精选的「软件测试」资料包

🔥 教程推荐:火遍全网的《软件测试》教程  

📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!

1、什么是mock测试

1.png

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

2、为什么要进行Mock测试

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

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

3、Mock适用场景

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

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

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

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

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

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

4、代码实例

新建测试工程

 
  1. package com.echo.mockito;

  2.  

  3. public class demo {

  4.  

  5.     //新建一个测试方法

  6.     public int add(int a,  int b){

  7.         return a + b;

  8.     }

  9. }

构建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、测试方法说明

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

  • 打桩测试

  • 异常测试

  • 真实方法调用

 
  1. package com.echo.mockito;

  2.  

  3. import org.junit.jupiter.api.AfterEach;

  4. import org.junit.jupiter.api.Assertions;

  5. import org.junit.jupiter.api.BeforeEach;

  6. import org.junit.jupiter.api.Test;

  7. import org.mockito.Mock;

  8. import org.mockito.Mockito;

  9. import org.mockito.MockitoAnnotations;

  10. import org.mockito.Spy;

  11.  

  12.  

  13. class demoTest {

  14.  

  15.      @Mock

  16.      demo demo;

  17.      //测试前开启mock

  18.     @BeforeEach

  19.     void setUp() {

  20.         MockitoAnnotations.openMocks(this);

  21.     }

  22.  

  23.     @Test

  24.     void add() {

  25.         //mock 打桩,就是不管真实的方法如何执行,我们可以自行假设该方法执行的结果

  26.         //后续的测试都是基于打桩结果来走

  27.        // Mockito.when(demo.add(1,2)).thenReturn(4);

  28.        // Assertions.assertEquals(3,demo.add(1,2));

  29.         //当测试方法出现异常,测试方法  如果有try{}catch{} 则可以测试异常是否正常

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

  31.         //调用真实的方法

  32.         Mockito.when(demo.add(1,1)).thenCallRealMethod();

  33.         Assertions.assertEquals(2,demo.add(1,1));

  34.     }

  35.  

  36.     @AfterEach

  37.     void after(){

  38.         System.out.println("测试结束");

  39.     }

  40. }

8、Mock静态方法

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

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

 
  1.   <!--   <dependency>

  2.             <groupId>org.mockito</groupId>

  3.             <artifactId>mockito-core</artifactId>

  4.             <version>4.6.1</version>

  5.         </dependency>

  6. -->

  7.         <dependency>

  8.             <groupId>org.mockito</groupId>

  9.             <artifactId>mockito-inline</artifactId>

  10.             <version>4.3.1</version>

  11.             <scope>test</scope>

  12.         </dependency>

 
  1. package com.echo.mockito.Util;

  2.  

  3. import org.junit.jupiter.api.Assertions;

  4. import org.junit.jupiter.api.BeforeEach;

  5. import org.junit.jupiter.api.Test;

  6. import org.mockito.MockedStatic;

  7. import org.mockito.Mockito;

  8.  

  9. import java.util.Arrays;

  10.  

  11. import static org.junit.jupiter.api.Assertions.*;

  12.  

  13. class StaticUtilsTest {

  14.  

  15.     @BeforeEach

  16.     void setUp() {

  17.     }

  18.  

  19.     // 有参静态方法构建

  20.     @Test

  21.     void range() {

  22.         MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);

  23.         //打桩

  24.         demo.when(()->StaticUtils.range(2,6)).thenReturn(Arrays.asList(10,11,12));

  25.         Assertions.assertTrue(StaticUtils.range(2,6).contains(11));

  26.     }

  27.     // 无参静态方法构建

  28.     @Test

  29.     void name() {

  30.         MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);

  31.         //打桩

  32.         demo.when(StaticUtils::name).thenReturn("dhmw");

  33.         Assertions.assertEquals("dhmw",StaticUtils.name());

  34.  

  35.     }

  36. }

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

提示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()。相当于是单例的。用完后就的释放,下一个方法才能接着使用。

 
  1.   @Test

  2.     void range() {

  3.         MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);

  4.         //打桩

  5.         demo.when(()->StaticUtils.range(2,6)).thenReturn(Arrays.asList(10,11,12));

  6.         Assertions.assertTrue(StaticUtils.range(2,6).contains(11));

  7.        //关闭

  8.         demo.close();

  9.     }

  10.     // 无参静态方法构建

  11.     @Test

  12.     void name() {

  13.         MockedStatic   demo = Mockito.mockStatic(StaticUtils.class);

  14.         //打桩

  15.         demo.when(StaticUtils::name).thenReturn("dhmw");

  16.         Assertions.assertEquals("dhmw",StaticUtils.name());

  17.         //关闭

  18.         demo.close();

  19.     }

9、提升测试覆盖率

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

业务代码如下:

 
  1. package com.echo.mockito.service.impl;

  2.  

  3. import com.echo.mockito.dao.UserDao;

  4. import com.echo.mockito.service.RegistrationService;

  5. import com.echo.mockito.vo.User;

  6. import org.springframework.beans.factory.annotation.Autowired;

  7.  

  8. import javax.xml.bind.ValidationException;

  9. import java.sql.SQLException;

  10.  

  11. public class RegistrationServiceImpl implements RegistrationService {

  12.      @Autowired

  13.      UserDao userDao;

  14.     @Override

  15.     public User register(String name, String phone) throws Exception {

  16.         if (name == null || name.length() == 0){

  17.             throw new ValidationException("name 不能为空");

  18.         }

  19.         if (phone == null || phone.length() ==0 ){

  20.             throw new ValidationException("phone 不能为空");

  21.         }

  22.         User user;

  23.         try {

  24.               user = userDao.save(name,phone);

  25.         }catch (Exception e){

  26.             throw  new Exception("SqlException thrown" + e.getMessage());

  27.         }

  28.         return user;

  29.     }

  30. }

 
  1. package com.echo.mockito.dao;

  2.  

  3. import com.echo.mockito.vo.User;

  4.  

  5. public class UserDao {

  6.  

  7.     public User save(String name,String phnoe){

  8.         User user = new User();

  9.         user.setName(name);

  10.         return user;

  11.     }

  12. }

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

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

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

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

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

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

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

 
  1. package com.echo.mockito.service.impl;

  2.  

  3. import com.echo.mockito.dao.UserDao;

  4. import com.echo.mockito.vo.User;

  5. import org.junit.jupiter.api.Assertions;

  6. import org.junit.jupiter.api.BeforeEach;

  7. import org.junit.jupiter.api.Test;

  8. import org.mockito.*;

  9.  

  10. import javax.xml.bind.ValidationException;

  11.  

  12. import java.sql.SQLException;

  13.  

  14. import static org.junit.jupiter.api.Assertions.*;

  15.  

  16. class RegistrationServiceImplTest {

  17.  

  18.   @InjectMocks     //RegistrationServiceImpl 实例中注入@Mock标记的类,此处是注入userDao

  19.     @Spy

  20.     private RegistrationServiceImpl registrationService;

  21.     @Mock

  22.     private  UserDao userDao;

  23.     @BeforeEach

  24.     void setUp() {

  25.         MockitoAnnotations.openMocks(this);

  26.     }

  27.  

  28.     @Test

  29.     void register() throws Exception {

  30.         // ------------------  第一种 name  异常情况   测试   start ------------------------

  31.         String name = null;

  32.         String phone = "1234";

  33.         try {

  34.           registrationService.register(name,phone);

  35.         }catch (Exception e){

  36.         Assertions.assertTrue(e instanceof ValidationException);

  37.         }

  38.  

  39.         // ------------------  name  异常情况   测试   end  ------------------------

  40.  

  41.         // ------------------  第二种 phone  异常情况   测试   start  ------------------------

  42.         name = "111";

  43.         phone = null;

  44.       try {

  45.         registrationService.register(name,phone);

  46.       }catch (Exception e){

  47.         Assertions.assertTrue(e instanceof ValidationException);

  48.       }

  49.         // ------------------  phone  异常情况   测试   start  ------------------------

  50.  

  51.  

  52.         // ------------------  第三种 userDao.save   正常情况   测试   start  ------------------------

  53.       name = "111";

  54.       phone = "111";

  55.         //正常保存测试  打桩  走真实的方法

  56.         Mockito.when(userDao.save(name,phone)).thenCallRealMethod();

  57.         User user =  registrationService.register(name,phone);

  58.         Assertions.assertEquals("111",user.getName());

  59.         // ------------------  userDao.save   正常情况   测试   end  ------------------------

  60.  

  61.         // ------------------   第四种 userDao.save   异常情况   测试   start  ------------------------

  62.       //异常保存测试  打桩   通过thenThrow 抛出异常  测试异常是否被捕获

  63.       Mockito.when(userDao.save(name,phone)).thenThrow(new RuntimeException());

  64.       try {

  65.          registrationService.register(name,phone);

  66.       }catch (Exception e){

  67.         Assertions.assertTrue(e instanceof Exception);

  68.       }

  69. // ------------------  userDao.save   异常情况   测试   end  ------------------------

  70.     }

  71. }

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

测试覆盖率方法如下:

9.png

测试结果如下:

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

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

11.png

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

12.png

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

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

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

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

问题:

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

 
  1.     @Test

  2.     void register() throws Exception {

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

最后我邀请你进入我们的【软件测试学习交流群:785128166】, 大家可以一起探讨交流软件测试,共同学习软件测试技术、面试等软件测试方方面面,还会有免费直播课,收获更多测试技巧,我们一起进阶Python自动化测试/测试开发,走向高薪之路

作为一个软件测试的过来人,我想尽自己最大的努力,帮助每一个伙伴都能顺利找到工作。所以我整理了下面这份资源,现在免费分享给大家,有需要的小伙伴可以关注【公众号:程序员二黑】自提!

这篇关于Mock测试详细教程入门这一篇就够了的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/939086

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

性能测试介绍

性能测试是一种测试方法,旨在评估系统、应用程序或组件在现实场景中的性能表现和可靠性。它通常用于衡量系统在不同负载条件下的响应时间、吞吐量、资源利用率、稳定性和可扩展性等关键指标。 为什么要进行性能测试 通过性能测试,可以确定系统是否能够满足预期的性能要求,找出性能瓶颈和潜在的问题,并进行优化和调整。 发现性能瓶颈:性能测试可以帮助发现系统的性能瓶颈,即系统在高负载或高并发情况下可能出现的问题

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

字节面试 | 如何测试RocketMQ、RocketMQ?

字节面试:RocketMQ是怎么测试的呢? 答: 首先保证消息的消费正确、设计逆向用例,在验证消息内容为空等情况时的消费正确性; 推送大批量MQ,通过Admin控制台查看MQ消费的情况,是否出现消费假死、TPS是否正常等等问题。(上述都是临场发挥,但是RocketMQ真正的测试点,还真的需要探讨) 01 先了解RocketMQ 作为测试也是要简单了解RocketMQ。简单来说,就是一个分

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

【测试】输入正确用户名和密码,点击登录没有响应的可能性原因

目录 一、前端问题 1. 界面交互问题 2. 输入数据校验问题 二、网络问题 1. 网络连接中断 2. 代理设置问题 三、后端问题 1. 服务器故障 2. 数据库问题 3. 权限问题: 四、其他问题 1. 缓存问题 2. 第三方服务问题 3. 配置问题 一、前端问题 1. 界面交互问题 登录按钮的点击事件未正确绑定,导致点击后无法触发登录操作。 页面可能存在

业务中14个需要进行A/B测试的时刻[信息图]

在本指南中,我们将全面了解有关 A/B测试 的所有内容。 我们将介绍不同类型的A/B测试,如何有效地规划和启动测试,如何评估测试是否成功,您应该关注哪些指标,多年来我们发现的常见错误等等。 什么是A/B测试? A/B测试(有时称为“分割测试”)是一种实验类型,其中您创建两种或多种内容变体——如登录页面、电子邮件或广告——并将它们显示给不同的受众群体,以查看哪一种效果最好。 本质上,A/B测

数论入门整理(updating)

一、gcd lcm 基础中的基础,一般用来处理计算第一步什么的,分数化简之类。 LL gcd(LL a, LL b) { return b ? gcd(b, a % b) : a; } <pre name="code" class="cpp">LL lcm(LL a, LL b){LL c = gcd(a, b);return a / c * b;} 例题:

Java 创建图形用户界面(GUI)入门指南(Swing库 JFrame 类)概述

概述 基本概念 Java Swing 的架构 Java Swing 是一个为 Java 设计的 GUI 工具包,是 JAVA 基础类的一部分,基于 Java AWT 构建,提供了一系列轻量级、可定制的图形用户界面(GUI)组件。 与 AWT 相比,Swing 提供了许多比 AWT 更好的屏幕显示元素,更加灵活和可定制,具有更好的跨平台性能。 组件和容器 Java Swing 提供了许多

【IPV6从入门到起飞】5-1 IPV6+Home Assistant(搭建基本环境)

【IPV6从入门到起飞】5-1 IPV6+Home Assistant #搭建基本环境 1 背景2 docker下载 hass3 创建容器4 浏览器访问 hass5 手机APP远程访问hass6 更多玩法 1 背景 既然电脑可以IPV6入站,手机流量可以访问IPV6网络的服务,为什么不在电脑搭建Home Assistant(hass),来控制你的设备呢?@智能家居 @万物互联