当前位置: 首页 > news >正文

JUNIT5+Mockito单元测试

文章目录

  • 1、前言
  • 2、Maven依赖
    • 2.1 JDK21+SpringBoot版本基于3.1.0
    • 2.2 JDK17+SpringBoot版本基于2.2.5.RELEASE
  • 3、业务代码
  • 4、单元测试

1、前言

之前写过一篇使用testMe自动生成单元测试用例,使用的是junit4来编写的单元测试用例,目前很多新项目都已经使用JDK11+以及SpringBoot3+。本次基于junit5+Mockito来编写单元测试。

2、Maven依赖

2.1 JDK21+SpringBoot版本基于3.1.0

SpringBoot依赖

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.1.0</version><relativePath/> <!-- lookup parent from repository --></parent>
 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.objenesis</groupId><artifactId>objenesis</artifactId></exclusion></exclusions></dependency>

mockito依赖

<!--junit5单元测试--><dependency><groupId>org.mockito</groupId><artifactId>mockito-junit-jupiter</artifactId><version>5.3.1</version></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>5.3.1</version></dependency>

lombok依赖

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>edge-SNAPSHOT</version></dependency>

2.2 JDK17+SpringBoot版本基于2.2.5.RELEASE

SpringBoot依赖

<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.5.RELEASE</version></parent>

Junit依赖

<dependency><groupId>org.junit.jupiter</groupId><artifactId>junit-jupiter</artifactId><version>5.8.2</version></dependency>

mockito依赖

<dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>5.2.0</version><exclusions><exclusion><groupId>net.bytebuddy</groupId><artifactId>byte-buddy</artifactId></exclusion><exclusion><groupId>net.bytebuddy</groupId><artifactId>byte-buddy-agent</artifactId></exclusion></exclusions></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-junit-jupiter</artifactId><version>5.2.0</version></dependency><dependency><groupId>net.bytebuddy</groupId><artifactId>byte-buddy</artifactId><version>1.14.1</version></dependency><dependency><groupId>net.bytebuddy</groupId><artifactId>byte-buddy-agent</artifactId><version>1.14.1</version></dependency>

lombok依赖

<dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.30</version></dependency>

3、业务代码

package com.summer.toolkit.mock;import com.alibaba.fastjson.JSON;
import com.summer.toolkit.dto.UserDto;
import com.summer.toolkit.exception.BizException;
import com.summer.toolkit.executor.DefaultThreadFactory;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.*;@Slf4j
@Service
public class UserServiceImpl implements UserService {@Resourceprivate UserManager userManager;@Overridepublic Long createUser(UserDto userDto) {log.info("创建用户入参:{}", JSON.toJSONString(userDto));String name = userDto.getUsername();if (StringUtils.isBlank(name)) {log.error("用户名称不能为空");throw new BizException("用户名称不能为空");}Long id = userManager.createUser(userDto);log.info("创建用户出参:{}", id);return id;}@Overridepublic Boolean updateUser(UserDto userDto) {log.info("更新用户入参:{}", JSON.toJSONString(userDto));Long id = userDto.getId();String name = userDto.getUsername();if (Objects.isNull(id)) {log.error("用户主键不能为空");throw new BizException("用户主键不能为空");}if (StringUtils.isBlank(name)) {log.error("用户名称不能为空");throw new BizException("用户名称不能为空");}UserDto user = userManager.getUser(userDto);if (Objects.isNull(user)) {log.error("用户不存在");throw new BizException("用户不存在");}Boolean result = userManager.updateUser(userDto);log.info("更新用户出参:{}", result);return result;}@Overridepublic UserDto getUser(UserDto userDto) {log.info("获取用户入参:{}", JSON.toJSONString(userDto));Long id = userDto.getId();if (Objects.isNull(id)) {log.error("用户主键不能为空");throw new BizException("用户主键不能为空");}UserDto user = userManager.getUser(userDto);log.info("获取用户出参:{}", user);return user;}@Overridepublic Boolean batchCreateUser(List<UserDto> list) {log.info("批量创建用户入参:{}", JSON.toJSONString(list));if (CollectionUtils.isEmpty(list)) {log.error("入参集合不能为空");throw new BizException("入参集合不能为空");}int size = 10;long keepAliveTime = 60;long start = System.currentTimeMillis();BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(10000);ThreadFactory threadFactory = new DefaultThreadFactory("executor");ExecutorService executorService= new ThreadPoolExecutor(size, size, keepAliveTime, TimeUnit.MINUTES, workQueue, threadFactory);List<CompletableFuture<Boolean>> futureList = new ArrayList<>();for (UserDto userDto : list) {CompletableFuture<Boolean> future = CompletableFuture.supplyAsync(() -> {log.info("当前线程名称:{}", Thread.currentThread());try {Long id = userManager.createUser(userDto);TimeUnit.SECONDS.sleep(3L);log.info("线程:{} id={} done", Thread.currentThread(), id);return Boolean.TRUE;} catch (InterruptedException e) {log.error("创建用户异常:{}", e.getMessage(), e);return Boolean.FALSE;}}, executorService);futureList.add(future);}Boolean result = Boolean.TRUE;try {CompletableFuture.allOf(futureList.toArray(new CompletableFuture[0])).get(10, TimeUnit.SECONDS);for (CompletableFuture<Boolean> future : futureList) {Boolean back = future.get();if (Boolean.FALSE.equals(back)) {result = Boolean.FALSE;}}} catch (Exception e) {log.error("创建用户异常:{}", e.getMessage(), e);result = Boolean.FALSE;}long end = System.currentTimeMillis();log.info("批量创建用户耗时:{}", (end - start));log.info("批量创建用户出参:{}", result);return result;}}

4、单元测试

package com.summer.toolkit.service;import com.summer.toolkit.dto.UserDto;
import com.summer.toolkit.exception.BizException;
import com.summer.toolkit.mock.UserManager;
import com.summer.toolkit.mock.UserServiceImpl;
import com.summer.toolkit.util.FileUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.condition.EnabledIfEnvironmentVariable;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockedStatic;
import org.mockito.Mockito;
import org.mockito.junit.jupiter.MockitoExtension;
import org.slf4j.Logger;import java.util.ArrayList;
import java.util.Date;
import java.util.List;import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;@ExtendWith(MockitoExtension.class)
@EnabledIfEnvironmentVariable(named = "DEBUG", matches = "true")
public class UserServiceTest {@Mockprivate Logger log;@Mockprivate UserManager userManager;@InjectMocksprivate UserServiceImpl userService;@Testpublic void testCreateUser() {// 模拟依赖方法Mockito.when(userManager.createUser(any())).thenReturn(Long.valueOf(1));// 调用被测方法UserDto userDto = this.buildUserDto();Long result = userService.createUser(userDto);// 验证方法结果Long expect = 1L;Assertions.assertEquals(expect, result);// 验证方法是否被调用Mockito.verify(userManager).createUser(userDto);// 验证依赖对象只有这一个Mockito.verifyNoMoreInteractions(userManager);}@Testpublic void testUpdateUser() {// 模拟依赖方法Mockito.when(userManager.updateUser(any())).thenReturn(Boolean.TRUE);Mockito.when(userManager.getUser(any())).thenReturn(new UserDto());// 调用被测方法UserDto userDto = this.buildUserDto();userDto.setId(1L);Boolean result = userService.updateUser(userDto);// 验证方法结果Assertions.assertEquals(Boolean.TRUE, result);// 验证方法是否被调用Mockito.verify(userManager).getUser(any());Mockito.verify(userManager).updateUser(any());}@Testpublic void testGetUser() {// 模拟依赖方法Mockito.when(userManager.getUser(any())).thenReturn(new UserDto());// 调用被测方法UserDto userDto = this.buildUserDto();userDto.setId(1L);UserDto result = userService.getUser(userDto);// 验证方法结果Assertions.assertNotNull(result);// 验证方法是否被调用Mockito.verify(userManager).getUser(userDto);}@Testpublic void testBatchCreateUser() {// 模拟依赖方法,指定单个异常类型Mockito.when(userManager.createUser(any())).thenThrow(BizException.class);// 调用被测方法List<UserDto> param = new ArrayList<>();UserDto userDto = this.buildUserDto();param.add(userDto);Boolean result = userService.batchCreateUser(param);// 验证方法结果Assertions.assertEquals(Boolean.FALSE, result);// 验证方法是否被调用,默认一次Mockito.verify(userManager).createUser(userDto);// 验证方法是否被调用了1次Mockito.verify(userManager, Mockito.times(1)).createUser(any());}@Testpublic void testBatchCreateUserTimes() {// 模拟依赖方法,指定单个异常类型Mockito.when(userManager.createUser(any())).thenReturn(1L);// 调用被测方法List<UserDto> param = new ArrayList<>();UserDto userDto = this.buildUserDto();param.add(userDto);param.add(userDto);param.add(userDto);Boolean result = userService.batchCreateUser(param);// 验证方法结果Assertions.assertEquals(Boolean.TRUE, result);// 验证方法是否被调用了3次Mockito.verify(userManager, Mockito.times(3)).createUser(any());}@Testpublic void testFileUtils() {// 构建对象List<String> list = new ArrayList<>();list.add("1");list.add("2");// 模拟对应的类// JDK11及以上版本中,try块中的变量可以在外部声明MockedStatic<FileUtils> mocked = Mockito.mockStatic(FileUtils.class);try (mocked) {// 模拟依赖静态方法mocked.when(() -> FileUtils.readFileAllLines(anyString())).thenReturn(list);// 调用被测方法List<String> lines = FileUtils.readFileAllLines(anyString());// 验证方法结果Assertions.assertEquals(list.size(), lines.size());} catch (Exception e) {log.error("模拟静态方法异常:{}", e.getMessage(), e);}}/*** 构建用户数据传输对象** @return UserDto 返回构建好的用户数据传输对象*/private UserDto buildUserDto() {UserDto userDto = new UserDto();userDto.setUsername("小明");userDto.setBirthday(new Date());userDto.setAddress("北京市大兴区亦庄经济开发区");userDto.setComment("加麻加辣");userDto.setGender(1);return userDto;}}

相关文章:

JUNIT5+Mockito单元测试

文章目录 1、前言2、Maven依赖2.1 JDK21SpringBoot版本基于3.1.02.2 JDK17SpringBoot版本基于2.2.5.RELEASE 3、业务代码4、单元测试 1、前言 之前写过一篇使用testMe自动生成单元测试用例&#xff0c;使用的是junit4来编写的单元测试用例&#xff0c;目前很多新项目都已经使用…...

【C#】【SAP2000】读取SAP2000中所有Frame对象的应力比到Grasshopper中

if (build true) {// 连接到正在运行的 SAP2000// 使用 System.Runtime.InteropServices.Marshal.GetActiveObject 方法获取正在运行的 SAP2000 实例cOAPI mySapObject (cOAPI)System.Runtime.InteropServices.Marshal.GetActiveObject("CSI.SAP2000.API.SapObject"…...

一台服务器部署两个独立的mysql实例

&#x1f341;博主简介&#xff1a; &#x1f3c5;云计算领域优质创作者 &#x1f3c5;2022年CSDN新星计划python赛道第一名 &#x1f3c5;2022年CSDN原力计划优质作者 &#x1f3c5;阿里云ACE认证高级工程师 &#x1f3c5;阿里云开发者社区专…...

SpringBoot(Lombok + Spring Initailizr + yaml)

1.Lombok 1.基本介绍 2.应用实例 1.pom.xml 引入Lombok&#xff0c;使用版本仲裁 <!--导入springboot父工程--><parent><artifactId>spring-boot-starter-parent</artifactId><groupId>org.springframework.boot</groupId><version&g…...

数据库基础知识超详细解析~‍(进阶/复习版)

文章目录 前言一、数据库的操作1.登入数据库2.创建数据库3.显示当前数据库4.使用数据库5.删除数据库 二、常用数据类型三、数据库的约束1约束类型2NULL约束3UNIQUE:唯一约束4DEFAULT&#xff1a;默认值约束5 PRIMARY KEY&#xff1a;主键约束6 FOREIGN KEY&#xff1a;外键约束…...

创建对象的方法有哪些

创建对象的方法主要取决于你使用的编程语言和上下文。下面我将列出一些主流编程语言中创建对象的方法&#xff1a; Python: 使用类定义和__init__方法&#xff1a; pythonclass MyClass: def __init__(self, name): self.name nameobj MyClass("Alice") 1.使用工厂…...

Oracle 10g字符编码

pl/sql developer查询数据时出现乱码&#xff0c;主要检查如下&#xff1a; 1、检查服务器编码 select * from v$nls_parameters;select * from nls_database_parameters;select userenv(language) from dual; 2、查看数据库可用字符集参数设置 select * from v$nls_valid_val…...

掌握抽象基础之20个必备原则,看完你还不会,你打我

抽象基础之20个必备原则 1. 面向对象编程&#xff08;OOP&#xff09;中抽象原则背后的基本思想是什么&#xff1f;2.抽象和封装的区别&#xff1f;3.提供一个现实生活中说明抽象的例子4.在编程语言中如何实现抽象&#xff1f;5.定义抽象类6.提供一个抽象类的真实世界场景7.解释…...

设计模式 -- 2:策略模式

目录 总结部分&#xff1a;策略模式的优点部分代码部分 总结部分&#xff1a; 策略模式和简单工厂模式很像 区别在于 简单工厂模式 需求的是由工程创造的类 去给客户直接答案 而策略模式在于 我有主体 一个主体 根据策略的不同来进行不同的计算 我的主体就负责收钱 然后调度相…...

【快速上手ProtoBuf】proto 3 语法详解

1 &#x1f351;字段规则&#x1f351; 消息的字段可以⽤下⾯⼏种规则来修饰&#xff1a; singular &#xff1a;消息中可以包含该字段零次或⼀次&#xff08;不超过⼀次&#xff09;。 proto3 语法中&#xff0c;字段默认使⽤该规则。repeated &#xff1a;消息中可以包含该…...

人工智能的幽默“失误”

人工智能迷惑行为大赏 随着ChatGPT热度的攀升&#xff0c;越来越多的公司也相继推出了自己的AI大模型&#xff0c;如文心一言、通义千问等。各大应用也开始内置AI玩法&#xff0c;如抖音的AI特效&#xff5e;在使用过程中往往会遇到一些问题&#xff0c;让你不得不怀疑&#x…...

js的异步请求?

在 JavaScript 中&#xff0c;进行异步请求通常涉及到使用 XMLHttpRequest 对象或者更现代的 Fetch API 或 Axios 库。这些工具可以帮助我们向服务器发送请求并在后台获取数据&#xff0c;而不会阻塞页面的其他操作。 下面是一个简单的示例&#xff0c;演示如何使用原生的 XML…...

华润对象存储(OBS)工具类

目录 一、备注二、工具类三、对象存储放在内网&#xff0c;如何实现外网访问 一、备注 1、ObjectBasicInfo、ObjectDetailInfo、ResultBody这三个类可自行替换或者去掉 二、工具类 package com.xxx.util;import com.amazonaws.HttpMethod; import com.amazonaws.auth.AWSStat…...

强缓存和协商缓存的区别?

协商缓存和强缓存是 HTTP 缓存机制中的两种不同的策略&#xff0c;用于减少网络请求并提高网页加载速度。它们之间的主要区别在于缓存的验证方式和服务器返回的响应头。 强缓存&#xff1a; 强缓存是基于过期时间&#xff08;Expires&#xff09;和缓存标识&#xff08;Cache…...

ChatGPT提问技巧——对抗性提示

ChatGPT提问技巧——对抗性提示 对抗性提示是一种允许模型生成能够抵御某些类型的攻击或偏差的文本的技术。这种技术可用于训练更健壮、更能抵御某些类型的攻击或偏差的模型。 要在 ChatGPT 中使用对抗性提示&#xff0c;应为模型提供一个提示&#xff0c;该提示的设计应使模…...

openGauss使用BenchmarkSQL进行性能测试(上)

一、前言 本文提供openGauss使用BenchmarkSQL进行性能测试的方法和测试数据报告。 BenchmarkSQL&#xff0c;一个JDBC基准测试工具&#xff0c;内嵌了TPC-C测试脚本&#xff0c;支持很多数据库&#xff0c;如PostgreSQL、Oracle和Mysql等。 TPC-C是专门针对联机交易处理系统…...

Java的线程池机制

Java的线程池机制是用来管理和调度多个线程的工具。通过线程池&#xff0c;可以避免频繁地创建和销毁线程&#xff0c;提高线程的复用率&#xff0c;减少资源消耗。 Java中提供了几种不同类型的线程池&#xff1a; 1、FixedThreadPool&#xff08;固定大小线程池&#xff09;…...

EasyCode 插件的具体使用

前言 EasyCode 是基于IntelliJ IDEA Ultimate版开发的一个代码生成插件&#xff0c;主要通过自定义模板&#xff08;基于velocity&#xff09;来生成各种你想要的代码。通常用于生成Entity、Dao、Service、Controller。如果你动手能力强还可以用于生成HTML、JS、PHP等代码。理…...

Ypay源支付6.9无授权聚合免签系统可运营源码

YPay是一款专为个人站长设计的聚合免签系统&#xff0c;YPay基于高性能的ThinkPHP 6.1.2 Layui PearAdmin架构&#xff0c;提供了实时监控和管理的功能&#xff0c;让您随时随地掌握系统运营情况。 说明 Ypay源支付6.9无授权聚合免签系统可运营源码 已搭建测试无加密版本…...

SpringBoot+Vue项目报错(问题已解决)

1、错误日志 2、分析原因&#xff1a; JWT strings must contain exactly 2 period characters. Found: 0 JWT字符串必须包含2个句号字符。发现:0 分析&#xff1a;可以判断出大概可能是token格式出现了问题 3、参考 http://t.csdnimg.cn/hfEiY 4、检查后端代码是否出现问…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

【Oracle APEX开发小技巧12】

有如下需求&#xff1a; 有一个问题反馈页面&#xff0c;要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据&#xff0c;方便管理员及时处理反馈。 我的方法&#xff1a;直接将逻辑写在SQL中&#xff0c;这样可以直接在页面展示 完整代码&#xff1a; SELECTSF.FE…...

Day131 | 灵神 | 回溯算法 | 子集型 子集

Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 笔者写过很多次这道题了&#xff0c;不想写题解了&#xff0c;大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...

(二)原型模式

原型的功能是将一个已经存在的对象作为源目标,其余对象都是通过这个源目标创建。发挥复制的作用就是原型模式的核心思想。 一、源型模式的定义 原型模式是指第二次创建对象可以通过复制已经存在的原型对象来实现,忽略对象创建过程中的其它细节。 📌 核心特点: 避免重复初…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

安卓基础(aar)

重新设置java21的环境&#xff0c;临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的&#xff1a; MyApp/ ├── app/ …...

【JVM】Java虚拟机(二)——垃圾回收

目录 一、如何判断对象可以回收 &#xff08;一&#xff09;引用计数法 &#xff08;二&#xff09;可达性分析算法 二、垃圾回收算法 &#xff08;一&#xff09;标记清除 &#xff08;二&#xff09;标记整理 &#xff08;三&#xff09;复制 &#xff08;四&#xff…...

Git常用命令完全指南:从入门到精通

Git常用命令完全指南&#xff1a;从入门到精通 一、基础配置命令 1. 用户信息配置 # 设置全局用户名 git config --global user.name "你的名字"# 设置全局邮箱 git config --global user.email "你的邮箱example.com"# 查看所有配置 git config --list…...

Ubuntu系统复制(U盘-电脑硬盘)

所需环境 电脑自带硬盘&#xff1a;1块 (1T) U盘1&#xff1a;Ubuntu系统引导盘&#xff08;用于“U盘2”复制到“电脑自带硬盘”&#xff09; U盘2&#xff1a;Ubuntu系统盘&#xff08;1T&#xff0c;用于被复制&#xff09; &#xff01;&#xff01;&#xff01;建议“电脑…...