【单例测试】Mockito实战

目录

  • 一、项目介绍
  • 二、业务代码
    • 2.1 导入依赖
    • 2.2 entity
    • 2.3 Dao
    • 2.4 业务代码
  • 三、单元测试
    • 3.1 生成Test方法
    • 3.2 引入测试类
    • 3. 3 测试前准备
    • 3.4 测试
      • 3.4.1 name和phone参数校验
      • 3.4.2 测试数据库访问
    • 3.4.3 数据库反例
  • 总结

前面我们提到了《【单元测试】一文读懂java单元测试》
简单介绍了《【单元测试】单元测试之Mockito的使用》

今天一起来看看项目中mockito怎么用的

项目源码GitHub

一、项目介绍

在这里插入图片描述
这个案例比较简单,模拟了一个数据统计系统,由地推员输入客户的姓名和手机号,系统根据客户的手机号和归属地和所属的运营商将客户群体分组,分配给相应的销售组,最后构建用户对象存入数据表。

二、业务代码

2.1 导入依赖

<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.8.2</version>
</dependency>
<!--        <dependency>-->
<!--            <groupId>org.mockito</groupId>-->
<!--            <artifactId>mockito-core</artifactId>-->
<!--            <version>4.3.1</version>-->
<!--        </dependency>-->
<dependency><groupId>org.mockito</groupId><artifactId>mockito-inline</artifactId><version>4.3.1</version><scope>test</scope>
</dependency>

注意:mockito-inlinemockito-core不能同时打开

2.2 entity

PhoneNumber :

public class PhoneNumber {private final String number;private final String pattern = "^0?[1-9]{2,3}-?\\d{8}$";public String getNumber() {return number;}//在含参构造器中进行参数校验public PhoneNumber (String number) throws ValidationException {if (number == null) {throw new ValidationException("number 不能为空");} else if (isValid(number)) {throw new ValidationException("number 格式错误");}this.number = number;}private boolean isValid(String number) {return number.matches(pattern);}private static String getAreaCode(String number) {//具体实现逻辑return "a";}private static String getOperatorCode(String number) {//具体实现逻辑return "b";}
}

SalesRep:

public class SalesRep {private String repId;public SalesRep(String repId) {this.repId = repId;}public String getRepId() {return repId;}public void setRepId(String repId) {this.repId = repId;}
}

User:

public class User {private String name;private String phone;private String repId;public User() {}public User(String name, String phone, String repId) {this.name = name;this.phone = phone;this.repId = repId;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getPhone() {return phone;}public void setPhone(String phone) {this.phone = phone;}public String getRepId() {return repId;}public void setRepId(String repId) {this.repId = repId;}
}

2.3 Dao

UserDao :

public class UserDao {public User save(String name, String phone, String repId) throws SQLException {return new User(name, phone, repId);}
}

SalesDao:

public class SalesDao {public SalesRep findRep(String areaCode, String operatorCode) {if ("a".equals(areaCode) && "b".equals(operatorCode)) {return new SalesRep("Echo");}return null;}
}

2.4 业务代码

RegistrationServiceImpl:

public class RegistrationServiceImpl implements RegistrationService {SalesDao salesDao = new SalesDao();UserDao userDao = new UserDao();@Overridepublic User register(String name, String phone) throws Exception {// 参数校验if (name == null || name.length() == 0) {throw new ValidationException("number 不能为空");}if (phone == null || !isValid(phone)) {throw new ValidationException("phone 格式错误");}// 获取手机号归属地编号和运营商编号 然后通过编号找到区域内是 SalesRepString areaCode = FindUtils.getAreaCode(phone);String operatorCode = FindUtils.getOperatorCode(phone);User user;try {SalesRep rep = salesDao.findRep(areaCode, operatorCode);// 最后创建用户,落盘,然后返回user = userDao.save(name, phone, rep.getRepId());} catch (SQLException e) {throw new DAOException("SQLException thrown " + e.getMessage());}return user;}private boolean isValid(String phone) {String pattern = "^(13[0-9]|14[5|7]|15[0|1|2|3|4|5|6|7|8|9]|18[0|1|2|3|5|6|7|8|9])\\d{8}$";boolean flag = phone.matches(pattern);return flag;}}

其他代码见GitHub

三、单元测试

3.1 生成Test方法

1)点击我们需要生成测试的类,右击鼠标Generate->Test
在这里插入图片描述

2)选择对应的Junit版本和测试方法

在这里插入图片描述
3)这样在test目录下生成测试类
在这里插入图片描述

3.2 引入测试类

使用@Spy注解引入RegistrationServiceImpl这个待测试的类

在这里插入图片描述

这里有两个数据库操作salesDao.findRep和userDao.save,现在模拟salesDao正例操作,userDao反例操作,抛出异常

在这里插入图片描述

首先对userDao进行mock,模拟出对象,进行打桩

在这里插入图片描述
@InjectMocks注解的作用是将Mock出来的UserDao对象注入到RegistrationServiceImpl类当中

3. 3 测试前准备

引入@BeforeEach前置处理器,开启@Spy和@Mock

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

3.4 测试

3.4.1 name和phone参数校验

在这里插入图片描述
如果是空就或者0抛出异常

@Test
void register() throws Exception {String name = null;String phone = "15012345678";try {registrationService.register(name, phone);Assertions.fail("这里说明程序挂了");// 如果执行代码能快速定位} catch (Exception e) {Assertions.assertTrue(e instanceof ValidationException);}
}

这里name为null,会抛出异常,catch会捕捉到异常,通过 Assertions.assertTrue去接收异常,Assertions.fail(“这里说明程序挂了”);用于定位异常;

在这里插入图片描述
鼠标右击第三个,以单元测试覆盖路运行

在这里插入图片描述
运行结果,单元测试行覆盖率只有27%,我们只测试了26、27行,绿色代表我们覆盖了的地方,红色代表我们没有覆盖的地方。

接下来,我们对phone这个参数进行测试,

@Test
void register() throws Exception {String name = null;String phone = "15012345678";try {registrationService.register(name, phone);Assertions.fail("这里说明程序挂了");// 如果执行代码能快速定位} catch (Exception e) {Assertions.assertTrue(e instanceof ValidationException);}name ="Hanson";phone = null;try {registrationService.register(name, phone);Assertions.fail("这里说明程序挂了");// 如果执行代码能快速定位} catch (Exception e) {Assertions.assertTrue(e instanceof ValidationException);}
}

测试:

在这里插入图片描述

单元测试行覆盖率变成了38%,

3.4.2 测试数据库访问

@Test
void register() throws Exception {String name = null;String phone = "15012345678";try {registrationService.register(name, phone);Assertions.fail("这里说明程序挂了");// 如果执行代码能快速定位} catch (Exception e) {Assertions.assertTrue(e instanceof ValidationException);}name ="Hanson";phone = null;try {registrationService.register(name, phone);Assertions.fail("这里说明程序挂了");// 如果执行代码能快速定位} catch (Exception e) {Assertions.assertTrue(e instanceof ValidationException);}phone = "15012345678";MockedStatic<FindUtils> staticService = Mockito.mockStatic(FindUtils.class);staticService.when(() -> FindUtils.getAreaCode("15012345678")).thenReturn("a");// 可以返回具体的操作staticService.when(() -> FindUtils.getOperatorCode("15012345678")).thenReturn("b");// 可以返回具体的操作// 数据库操作正常Mockito.when(salesDao.findRep("a","b")).thenCallRealMethod();Mockito.when(userDao.save(name,phone,"Hanson")).thenCallRealMethod();User user = registrationService.register(name, phone);Assertions.assertEquals("Hanson",user.getRepId());
}

测试结果:
在这里插入图片描述
单元测试行覆盖率从38%变成了88%,为什么不是100%呢?那是因为这些都是正例,所有catcah没有捕获到异常

3.4.3 数据库反例

我们通过thenThrow()方法返回SQLException模拟异常,通过catch捕捉异常进行断言

@Test
void register() throws Exception {String name = null;String phone = "15012345678";try {registrationService.register(name, phone);Assertions.fail("这里说明程序挂了");// 如果执行代码能快速定位} catch (Exception e) {Assertions.assertTrue(e instanceof ValidationException);}name ="Hanson";phone = null;try {registrationService.register(name, phone);Assertions.fail("这里说明程序挂了");// 如果执行代码能快速定位} catch (Exception e) {Assertions.assertTrue(e instanceof ValidationException);}phone = "15012345678";MockedStatic<FindUtils> staticService = Mockito.mockStatic(FindUtils.class);staticService.when(() -> FindUtils.getAreaCode("15012345678")).thenReturn("a");// 可以返回具体的操作staticService.when(() -> FindUtils.getOperatorCode("15012345678")).thenReturn("b");// 可以返回具体的操作// 数据库操作正常Mockito.when(salesDao.findRep("a","b")).thenCallRealMethod();Mockito.when(userDao.save(name,phone,"Hanson")).thenCallRealMethod();User user = registrationService.register(name, phone);Assertions.assertEquals("Hanson",user.getRepId());// 数据库操作异常Mockito.when(userDao.save(name,phone,"Hanson")).thenThrow(new SQLException());try {registrationService.register(name,phone);} catch (Exception e) {Assertions.assertTrue(e instanceof DAOException);}

测试:

在这里插入图片描述
单元测试方法覆盖率和行覆盖率都是100%,完毕

总结

首先,确定测试类,对测试类进行有引入,其次确定是否有其他对象的注入,例如demo中的salesDao和userDao两个对象,还有可能是一些Service,我们需要用@Spy@Mock两个注解将对象注入进来,并在测试类对象是添加@InjectMocks将对象注入到测试类对象中;

其次我们要开启mock和spy方法(使用前置处理器,MockitoAnnotations.openMocks(this);

测试方法正反例写,静态方法使用Mockito.mockStatic方法将静态方法所属的类mock出来,在对这个方法进行打桩;

对于try-catch,对结果进行分类,以异常捕获和未捕获进行校验;

Jnuit文章见《【单元测试】一文读懂java单元测试》
单元测试之Mockito见文章《【单元测试】单元测试之Mockito的使用》
觉得有用的话还请来个三连!!!

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

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

相关文章

IDEA Android新建项目基础

title: IDEA Android基础开发 search: 2024-03-16 tags: “#JavaAndroid开发” 一、构建基本项目 在使用 IDEA 进行基础的Android 开发时&#xff0c;我们可以通过IDEA自带的新建项目功能进行Android应用开发基础架构的搭建&#xff0c;可以直接找到 File --> New --> …

基于nodejs+vue学生作业管理系统python-flask-django-php

他们不仅希望页面简单大方&#xff0c;还希望操作方便&#xff0c;可以快速锁定他们需要的线上管理方式。基于这种情况&#xff0c;我们需要这样一个界面简单大方、功能齐全的系统来解决用户问题&#xff0c;满足用户需求。 课题主要分为三大模块&#xff1a;即管理员模块和学生…

上位机图像处理和嵌入式模块部署(qmacvisual轮廓查找)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面我们说过&#xff0c;图像的处理流程一般都是这样的&#xff0c;即灰度化-》降噪-》边缘检测-》二值化-》开闭运算-》轮廓检测。虽然前面的几个…

LeetCode 面试经典150题 14.最长公共前缀

题目&#xff1a; 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 思路&#xff1a; 代码&#xff1a; class Solution {public String longestCommonPrefix(String[] strs) {if (strs.length 0) {return &…

知攻善防应急靶场-Linux(2)

前言&#xff1a; 堕落了三个月&#xff0c;现在因为被找实习而困扰&#xff0c;着实自己能力不足&#xff0c;从今天开始 每天沉淀一点点 &#xff0c;准备秋招 加油 注意&#xff1a; 本文章参考qax的网络安全应急响应和知攻善防实验室靶场&#xff0c;记录自己的学习过程&am…

python绘图matplotlib——使用记录1

本博文来自于网络收集&#xff0c;如有侵权请联系删除 使用matplotlib绘图 1 常用函数汇总1.1 plot1.2 legend1.3 scatter1.4 xlim1.5 xlabel1.6 grid1.7 axhline1.7 axvspan1.8 annotate1.9 text1.10 title 2 常见图形绘制2.1 bar——柱状图2.2 barh——条形图2.3 hist——直…

flutter3_douyin:基于flutter3+dart3短视频直播实例|Flutter3.x仿抖音

flutter3-dylive 跨平台仿抖音短视频直播app实战项目。 全新原创基于flutter3.19.2dart3.3.0getx等技术开发仿抖音app实战项目。实现了类似抖音整屏丝滑式上下滑动视频、左右滑动切换页面模块&#xff0c;直播间进场/礼物动效&#xff0c;聊天等模块。 运用技术 编辑器&#x…

Netty剖析 - Why Netty

文章目录 Why NettyI/O 请求的两个阶段I/O 模型Netty 如何实现自己的 I/O 模型线程模型 - 事件分发器&#xff08;Event Dispather&#xff09;弥补 Java NIO 的缺陷更低的资源消耗网络框架的选型Netty 发展现状Netty 的使用 Why Netty I/O 模型、线程模型和事件处理机制优化&a…

php搭建websocket

1.项目终端执行命令&#xff1a;composer require topthink/think-worker 2.0.x 2.config多出三个配置文件&#xff1a; 3.当使用php think worker:gateway命令时&#xff0c;提示不支持Windows。 4.打包项目为zip格式 5.打包数据库 6.阿里云创建记录 7.宝塔面板新增站点…

Vue3 上手笔记

1. Vue3简介 2020年9月18日&#xff0c;Vue.js发布版3.0版本&#xff0c;代号&#xff1a;One Piece&#xff08;n 经历了&#xff1a;4800次提交、40个RFC、600次PR、300贡献者 官方发版地址&#xff1a;Release v3.0.0 One Piece vuejs/core 截止2023年10月&#xff0c;最…

网盘——数据库操作

关于网盘的数据库模块&#xff0c;主要有以下几个内容&#xff1a;定义数据库操作类、将数据库操作类定义成单例模式、数据库操作 数据库是在Qt里面&#xff0c;定义成操作类&#xff0c;专门用这个类产生对象&#xff0c;对数据库实现操作&#xff0c;那么我们在产生对象的时…

BMS设计中的短路保护和MOSFET选型(下)

二、MOSFET参数 1、电气参数 (1)VGS :加在栅源两极之间的最大电压,一般为:-20V-+20V。 VGS额定电压是栅源两极间可以施加的最大电压。设定该额定电压的主要目的是防止电压过高导致的栅氧化层损伤。实际栅氧化层可承受的电压远高于额定电压,但是会随制造工艺的不同而改变…

01-机器学习概述

机器学习的定义 机器学习是一门从数据中研究算法的科学学科。 机器学习直白来讲&#xff0c; 就是根据已有的数据&#xff0c;进行算法选择&#xff0c;并基于算法和数据 构建模型&#xff0c;最终对未来进行预测。 机器学习就是一个模拟人决策过程的一种程序结构。 机器学…

PWM实现电机的正反转和调速以及TIM定时器

pwm.c #include "pwm.h"/* PWM --- PA2 --TIM2_CH3 //将电机信号控制一根接GND,一根接在PA2(TIM2_CH3)&#xff0c; 输出PWM控制电机快慢 TIM2挂在APB1 定时器频率&#xff1a;84MHZ*/ void Pwm_Init(void) {GPIO_InitTypeDef GPIO_InitStruct;TIM_TimeBaseInitT…

Django下载使用、文件介绍

【一】下载并使用 【1】下载框架 &#xff08;1&#xff09;注意事项 计算机名称不要出现中文python解释器版本不同可能会出现启动报错项目中所有的文件名称不要出现中文多个项目文件尽量不要嵌套,做到一项一夹 &#xff08;2&#xff09;下载 Django属于第三方模块&#…

STM32微控制器中,如何处理多个同时触发的中断请求?

在STM32微控制器中&#xff0c;处理多个同时触发的中断请求需要一个明确的中断优先级策略&#xff0c;以确保关键任务能够及时得到响应。STM32的中断控制器&#xff08;NVIC&#xff09;支持优先级分组&#xff0c;允许开发者为不同的中断设置抢占优先级和子优先级。本文将详细…

uniapp 打包后缺少maps模块和share模块的解决方案

缺失maps模块 我的应用 | 高德控制台 缺失share模块 QQ互联管理中心 微信开放平台

星云小窝项目1.0——项目介绍(一)

星云小窝项目1.0——项目介绍&#xff08;一&#xff09; 文章目录 前言1. 介绍页面2. 首页2.1. 游客模式2.2. 注册用户后 3. 星云笔记3.1. 星云笔记首页3.2. 星云笔记 个人中心3.2. 星云笔记 系统管理3.3. 星云笔记 文章展示3.3. 星云笔记 新建文章 4. 数据中心5. 交流评论6. …

Linux cp、mv命令显示进度条

1.advcpmv 平常使用cp 拷贝大文件时&#xff0c;看不到多久可以完成&#xff0c;虽然加上-v参数也只能看到正在拷贝文件&#xff0c;那就使用以下方法实现 git clone https://github.com/jarun/advcpmv.git cd advcpmv/ bash install.shmv ./advcp /usr/local/bin/ mv ./advmv …

SpringBoot3+Vue3项目的阿里云部署--将后端以及前端项目打包

一、后端&#xff1a;在服务器上制作成镜像 1.准备Dockerfile文件 # 基础镜像 FROM openjdk:17-jdk-alpine # 作者 MAINTAINER lixuan # 工作目录 WORKDIR /usr/local/lixuan # 同步docker内部的时间 RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ…