Junit 单元测试之错误和异常处理
错误和异常处理是测试中非常重要的部分。假设我们有一个服务,该服务从数据库中获取用户。现在,我们要考虑的错误场景是:数据库连接断开。
整体代码示例
首先,为了简化,我们让服务层就是简单的类,然后使用Id查找用户,这个和之前测试UserService接口不太一样哦:
@Service
public class UserService {@Autowiredprivate UserRepository userRepository;public User getUserById(Long id) {return userRepository.findById(id).orElse(null);}
}
现在,我们要模拟UserRepository
的行为,使其在尝试获取用户时引发一个异常。这里我们使用Mockito进行模拟:
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserServiceTest {//之前我们是定义了一个UserService接口,现在简化成UserService类了哈@InjectMocksprivate UserService userService;@Mockprivate UserRepository userRepository;@Beforepublic void setUp() throws Exception {MockitoAnnotations.initMocks(this);}//重点,后文详解!@Test(expected = DatabaseConnectionException.class)public void testGetUserByIdWithDbError() {when(userRepository.findById(anyLong())).thenThrow(new DatabaseConnectionException("Database connection failed!"));userService.getUserById(1L);}
}//重点,后文详解!
class DatabaseConnectionException extends RuntimeException {public DatabaseConnectionException(String message) {super(message);}
}
在上述测试中,我们模拟了userRepository.findById()
方法,使其抛出DatabaseConnectionException
异常。然后,我们在测试方法上使用@Test(expected = DatabaseConnectionException.class)
来表示我们期望该方法引发此异常。
这样,如果getUserById
方法在遇到此异常时没有正确处理,测试将失败。这确保了即使在面对意外的数据库问题时,我们的代码仍能按预期的方式运行(在这种情况下,按预期抛出异常)。
到底在模拟什么?到底在测试什么?
下面,我们进一步说明:
测试目标:这个测试的目标是确保当
userRepository.findById()
方法抛出DatabaseConnectionException
异常时,userService.getUserById()
方法也会抛出同样的异常。模拟异常:在这行代码中,我们指定了当
userRepository.findById()
被调用时,它应该抛出DatabaseConnectionException
异常。when(userRepository.findById(anyLong())).thenThrow(new DatabaseConnectionException("Database connection failed!"));
调用Service方法:接下来,我们调用了
userService.getUserById(1L)
。我们期望它在内部调用userRepository.findById()
(这在实际的UserService
实现中应该是这样的)。因此,由于我们已经模拟了userRepository.findById()
来抛出异常,所以userService.getUserById()
也应该会抛出这个异常。验证异常:
@Test(expected = DatabaseConnectionException.class)
注解表示我们期望这个测试方法在执行时会抛出DatabaseConnectionException
异常。如果这个方法执行完并没有抛出这个异常,那么测试将会失败。测试的目的:这个测试的目的并不是检查
userRepository.findById()
本身是否真的会抛出异常,而是检查当它抛出异常时,userService.getUserById()
是否会正确地传递这个异常。这可以帮助我们确保UserService
在处理异常时的行为是正确的。其实本质上来说,抛出异常和预期值的测试逻辑几乎是一样的,都是通过给定下层值,验证上层代码关系。
综上所述,这个测试确保了当底层UserRepository
出现数据库连接错误时,上层的UserService
可以正确地传递这个错误。这对于后续的异常处理很重要,例如:在Controller层将这个异常转化为一个友好的错误消息返回给用户。
什么时候测试失败?
在正常情况下,只要
Service
层确实调用了Repository
的方法,并且Repository
的方法抛出了RuntimeException
(或其子类),那么Service
层的调用方法也应该会收到并进一步抛出这个异常。
但是,以下几种情况可能导致测试不通过:
-
异常被吞没:如果
Service
层调用了Repository
的方法,但内部捕获了该异常并没有重新抛出,那么测试就会失败。例如:public User getUserById(Long id) {try {return userRepository.findById(id);} catch (DatabaseConnectionException e) {// 异常被吞没了return null;} }
-
调用的方法不正确:如果
Service
层没有调用预期的Repository
方法,而是调用了其他方法,或者完全没有调用,那么模拟的异常就不会被触发,导致测试失败。 -
模拟的不正确:如果在测试中模拟的方法或参数与实际调用的方法或参数不匹配,那么模拟的异常也不会被触发。例如,如果
Service
实际上是这样调用的:userRepository.findById(2L)
,但我们的模拟是这样的:when(userRepository.findById(1L))...
,那么异常就不会被触发。-
其他未预料到的异常:有时可能会有其他的未被预料到的异常被抛出,这也会导致测试失败。
-
因此,虽然大多数情况下,如果Repository
层方法抛出了异常,Service
层应该也会抛出,但还是存在一些情况导致测试不通过,这也是进行此类测试的原因。
Exception 异常类定义
class DatabaseConnectionException extends RuntimeException {public DatabaseConnectionException(String message) {super(message);}
}
DatabaseConnectionException
是一个自定义的异常类。在Java中,异常是用来表示程序运行中的问题或异常情况的对象。当某些问题发生时,通常会抛出(throw)一个异常。
这里,我们定义了一个继承自RuntimeException
的新异常类DatabaseConnectionException
。RuntimeException
是Java中所有非检查型异常的基类。所谓“非检查型”是指编译器不强制我们捕获或声明它。这与Exception
(检查型异常)相对。
关于DatabaseConnectionException
类的解释:
class DatabaseConnectionException extends RuntimeException
- 这表示我们正在定义一个名为DatabaseConnectionException
的新类,该类是RuntimeException
的子类。这意味着DatabaseConnectionException
继承了RuntimeException
的所有特性。
public DatabaseConnectionException(String message)
- 这是DatabaseConnectionException
类的构造方法。当我们创建DatabaseConnectionException
的新实例时,可以传递一个消息字符串给这个构造函数。
super(message);
- 这行代码调用了父类(RuntimeException
)的构造方法,并将message
传递给它。这样,当异常被抛出并捕获时,我们可以获取并显示这个消息。
这种自定义异常,通常在我们希望为特定的错误情况定义更具描述性的异常名时使用,或者当我们想为特定的异常情况添加更多上下文信息时使用,信息越多,测试反馈的效果越好,所以一般使用自定义异常,继承RuntimeException!下面我们讨论一下,为什么建议使用RuntimeException?
RuntimeException 使用意义
使用
RuntimeException
(非检查型异常)还是Exception
(检查型异常)来自定义数据库异常(或其他异常)是一个设计决策,并且这两者在Java中有不同的含义和用途。
下面是一些选择使用RuntimeException
的原因:
不需要显式处理:当方法中抛出非检查型异常时,调用该方法的代码不需要显式地处理异常(即不需要使用
try-catch
或在方法签名中使用throws
)。这使得代码更简洁,更易读。表示编程错误:非检查型异常通常用于表示编程错误,例如空指针异常、数组越界等。对于某些数据库异常,如配置错误,这可能是一个编程错误,因此使用
RuntimeException
可能更合适。强制开发者考虑异常处理策略:使用检查型异常会强制调用者处理异常,这可能会导致过多的
try-catch
块并使代码复杂化。而使用非检查型异常,开发者可以选择在何处处理异常,这通常会导致更好、更集中的异常处理策略。与现有框架兼容:许多现代Java框架,如Spring,倾向于使用非检查型异常,因为它们认为异常应该在应用程序的高层(如Controller或Service)中统一处理。
灵活性:有时,在开发过程的后期,可能会发现某些异常不再是关键的,不需要强制处理。对于非检查型异常,这意味着不需要修改方法签名或调用代码。
然而,这并不意味着总是应该选择非检查型异常。有时,如果你希望调用者必须处理某个特定的异常,使用检查型异常可能更合适。选择使用哪种异常是基于特定上下文和需求的决策。但在许多现代Java应用程序中,倾向于使用RuntimeException
因为它提供了更大的灵活性和简洁性。
总结
模拟异常的目的
- 验证代码在遇到异常时是否有正确的响应,例如是否抛出了预期的异常。
- 确保代码在异常情况下仍然能够维持预期的状态或行为。
- 单元测试通常关注隔离性,因此模拟异常可以确保在不涉及实际外部依赖的情况下,模拟各种可能的场景。
真正的数据库异常是不是Runtime异常
在Java中,数据库操作可能会抛出多种异常。其中,SQLException
是一个受检异常(checked exception)。
但在很多现代的框架中(如Spring),这些受检异常通常会被转换成运行时异常(runtime exceptions),这样可以使代码更为简洁,避免了过多的try-catch
块。
相关文章:
Junit 单元测试之错误和异常处理
错误和异常处理是测试中非常重要的部分。假设我们有一个服务,该服务从数据库中获取用户。现在,我们要考虑的错误场景是:数据库连接断开。 整体代码示例 首先,为了简化,我们让服务层就是简单的类,然后使用I…...
LockSupport-park和unpark编码实战
package com.nanjing.gulimall.zhouyimo.test;import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.LockSupport;/*** author zhou* version 1.0* date 2023/10/16 9:11 下午*/ public class LockSupportDemo {public static void main(String[] args) {…...
js深拷贝与浅拷贝
1.浅拷贝概念 浅拷贝是其属性与拷贝源对象的属性共享相同引用,当你更改源或副本时,也可能(可能说的是只针对引用数据类型)导致其他对象也发生更改。 特性: 会新创建一个对象,即objobj2返回fasle…...
Docker-harbor私有仓库部署与管理
搭建本地私有仓库 #首先下载 registry 镜像 docker pull registry #在 daemon.json 文件中添加私有镜像仓库地址 vim /etc/docker/daemon.json { "insecure-registries": ["20.0.0.50:5000"], #添加,注意用逗号结…...
ArcGIS笔记8_测量得到的距离单位不是米?一经度一纬度换算为多少米?
本文目录 前言Step 1 遇到测量结果以度为单位的情况Step 2 简单的笨办法转换为以米为单位Step 3 拓展:一经度一纬度换算为多少米 前言 有时我们会遇到这种情况,想在ArcGIS中使用测量工具测量一下某一段距离,但显示的测量结果却是某某度&…...
SpringBoot入门详解
目录 因何而生的SpringBoot 单体架构的捉襟见肘 SpringBoot的优点 快速入门 高曝光率的Annotation SpringBoot的工作机制 了解SpringBootApplication SpringBootConfiguration EnableAutoConfiguration 自动配置的幕后英雄:SpringFactoriesLoader Compon…...
数据分析案例-基于snownlp模型的MatePad11产品用户评论情感分析(文末送书)
🤵♂️ 个人主页:艾派森的个人主页 ✍🏻作者简介:Python学习者 🐋 希望大家多多支持,我们一起进步!😄 如果文章对你有帮助的话, 欢迎评论 💬点赞Ǵ…...
Leetcode刷题解析——904. 水果成篮
1. 题目链接:904. 水果成篮 2. 题目描述: 你正在探访一家农场,农场从左到右种植了一排果树。这些树用一个整数数组 fruits 表示,其中 fruits[i] 是第 i 棵树上的水果 种类 。 你想要尽可能多地收集水果。然而,农场的主…...
Spring Boot RESTful API
学习到接口部分了,记录一下 关于restful api感觉这篇文章讲的十分详细且通俗易懂一文搞懂什么是RESTful API - 知乎 (zhihu.com) Spring Boot 提供的 spring-boot-starter-web 组件完全支持开发 RESTful API ,提供了 GetMapping:处理get请求…...
k8s day04
昨日内容回顾: - configMap ---> cm 应用场景: 主要用于配置文件的持久化。 - secret 应用场景: 存储敏感数据,并非加密数据。 - pod探针(probe): - livenessProbe: 健康检查探针&#x…...
ESP32-IPS彩屏ST7789-Arduino-简单驱动
目的: 使ESP32能够驱动点亮ST7789显示屏 前提条件: ESP32 ST7789 (240 x240,IPS) 杜邦线 Arduino 过程: 0x00--接线 0x01--驱动: 彩屏驱动库 针对不同的彩屏驱动芯片,常用的 Arduino…...
高效工具类软件使用
高效工具类软件使用 目录概述需求: 设计思路实现思路分析1.Leanote2.Obsidian 的使用 参考资料和推荐阅读 Survive by day and develop by night. talk for import biz , show your perfect code,full busy,skip hardness,make a better result,wait for…...
批处理文件(.bat)中,dir与tree命令的效果
目录 dir命令 用法 操作 效果 dir /? dir dir D:\111\111_3 dir D:\111 *.mp4 dir D:\111 /ad dir D:\111 /ar dir D:\111 /s dir D:\111\111_3 >1bat.txt dir D:\111 >>1bat.txt tree命令 用法 操作 效果 tree /? tree tree D:\111\111_3 tree…...
STM32 ---- 再次学习STM32F103C8T6/STM32F409IGT6
目录 一、环境搭建及介绍 关于STM32基础介绍 新建工程 外设案例 LED流水灯 蜂鸣器 上拉电阻和下拉电阻知识 电压比较器 c语言基础知识 类型、结构体、枚举 类型int8_t int16_t int32_t 宏替换 #define 和typedef用法 结构体两种填充方法 和 命名规则 枚举用法 常用…...
UE4 EQS环境查询 学习笔记
EQS环境查询对应Actor的范围 EQS环境查询查询对应的类 查询到即有一个蓝色的球在Actor上,里面有位置信息等等 在行为树运行EQS,按键(‘)可以看到Player的位置已经被标记 运行对应的EQS在这里放如EQS就可以了 Generated Point&…...
计算机算法分析与设计(11)---贪心算法(活动安排问题和背包问题)
文章目录 一、贪心算法概述二、活动安排问题2.1 问题概述2.2 代码编写 三、背包问题3.1 问题描述3.2 代码编写 一、贪心算法概述 1. 贪心算法的定义:贪心算法是指在对问题求解时,总是做出在当前看来是最好的选择。也就是说,不从整体最优上加以…...
shell命令以及运行原理
Linux严格意义上说的是一个操作系统,我们称之为“核心(kernel)“ ,但我们一般用户,不能直接使用kernel。 而是通过kernel的“外壳”程序,也就是所谓的shell,来与kernel沟通。如何理解&a…...
MySQL进阶(再论JDBC)——JDBC编程思想的分析 JDBC的规范架构 JDBC相关的类分析
前言 SQL(Structured Query Language)是一种用于管理关系型数据库的标准化语言,它用于定义、操作和管理数据库中的数据。SQL是一种通用的语言,可以用于多种关系型数据库管理系统(RDBMS),如MySQ…...
rabbitMQ的知识点
RabbitMQ是一种消息队列软件,它实现了高度可靠的消息传递机制。RabbitMQ支持多种消息协议,包括AMQP、STOMP、MQTT等,比较灵活。以下是一些rabbitmq的知识点: 1. 消息队列:消息队列是一种分布式系统中广泛使用的通信模…...
EtherNet/IP 库卡机器人和EtherCAT倍福PLC总线协议连接案例
EtherNet/IP 是一种适合于工业环境和对时间要求比较苛刻的应用的网络。而远创智控YC-EIPM-ECT通讯网关,是一款自主研发的EtherNet/IP 从站功能的通讯网关。它不仅可以实现EtherNet/IP 和EtherCAT的无缝连接,还可以将EtherNet/IP 作为从站连接到EtherCAT总…...
微信小程序 uniapp+vue线上洗衣店业务管理系统演89iu2
本课题意在设计一种系统的、基于用户体验的线上洗衣服务模式,具有如下的研究意义: (1)为用户提供更简单、便捷的洗衣服务模式; (2)为智能柜的盈利模式提供了新的方向; (3)通过线上系统、智能柜与洗衣工厂结合的方式,为洗衣企业构建了一套节 省人力成本的…...
Maven项目,进行编译,使用idea的 编译功能,就是正常的,但是在终端中执行 mvn clean compile 报错
一、背景: Maven项目,进行编译,使用idea的 编译功能,就是正常的,但是在终端中执行 mvn clean compile 报错 报错信息: [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin…...
mssql还原数据库失败
标题: Microsoft SQL Server Management Studio ------------------------------ 服务器 "192.168.31.132" 的 附加数据库 失败。 (Microsoft.SqlServer.Smo) 有关帮助信息,请单击: https://go.microsoft.com/fwlink?ProdNameMicrosoftSQLServer&…...
Linux多线程编程- 无名信号量
简介 无名信号量(在 POSIX 环境下通常指 sem_t 类型的信号量)是用于同步和互斥的原语,它允许线程和进程按照预期的顺序执行,并确保对共享资源的安全访问。无名信号量与命名信号量的主要区别在于它们的可见性和生命周期。无名信号…...
【网络协议】聊聊DHCP和PXE 工作原理
DHCP 动态主机配置协议 对于每个主机来说,只要连接了网络,那么就会配置一个IP地址,那么这个IP地址,如果是手动配置的话,对于公司内部的人员来说都要找IT进行配置,这个太浪费人力物力了,所以解决…...
发现国内优秀的团队协作软件,帮助提高工作效率
中国有许多优秀的团队协作软件,它们在企业和组织中发挥着重要作用。 以下是一些最受欢迎的团队协作软件: 1、钉钉(DingTalk): 这是一款由阿里巴巴推出的企业级协作工具,旨在帮助企业和组织实现高效沟通和协作。钉钉提…...
LeetCode 面试题 08.12. 八皇后
文章目录 一、题目二、C# 题解 一、题目 设计一种算法,打印 N 皇后在 N N 棋盘上的各种摆法,其中每个皇后都不同行、不同列,也不在对角线上。这里的“对角线”指的是所有的对角线,不只是平分整个棋盘的那两条对角线。 注意&#…...
Excel 的下拉列表
可以将 Sheet6 隐藏,就更好地隐藏了来源。...
基于Effect的组件设计 | 京东云技术团队
Effect的概念起源 从输入输出的角度理解Effect https://link.excalidraw.com/p/readonly/KXAy7d2DlnkM8X1yps6L 编程中的Effect起源于函数式编程中纯函数的概念 纯函数是指在相同的输入下,总是产生相同的输出,并且没有任何副作用(side effect)的函数。…...
541. 反转字符串 II
541. 反转字符串 II class Solution { public:void Reverse(string& s, int start, int end){end--;while (start < end){swap(s[start], s[end]);start;end--;}}string reverseStr(string s, int k){int len s.size();for (int i 0; i < len; i 2 * k){if (i …...
淘宝客网站空间/百度关键词收录排名
JavaScript实现的一个动态查询表格,随着文本框中资料的改变,下边Table中的资料会自动筛选。 <% page language"java" contentType"text/html; charsetUTF-8" pageEncoding"UTF-8"%> <!DOCTYPE <html> <…...
建设b2c商城网站/百度指数人群画像哪里查询
1.什么是vuex?官方的理解是:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。Vuex 也集成到 Vue 的官方调试工具 devtools extension,提…...
库尔勒北京网站建设/seo建站公司推荐
letswave教程:脑电数据的时频分析/组平均与统计分析1 相关概念2 单被试分析2.1 时频分析2.1.1 连续小波变换2.1.2 基准线校正2.1.3 查看结果3 组分析3.1 总体平均3.1.1 降低采样率3.1.2 重命名3.1.3 数据集复制和合并3.1.4 平均3.1.5 查看结果3.2 统计分析3.2.1 假设…...
中国企业网安企在线/热狗seo外包
1.导出整个数据库 mysqldump -u 用户名 -p 数据库名 > 导出的文件名 mysqldump -u wcnc -p smgp_apps_wcnc > wcnc.sql 2.导出一个表 mysqldump -u 用户名 -p 数据库名 表名> 导出的文件名 mysqldump -u wcnc -p smgp_apps_wcnc users> wcnc_users.sql 3.导出一个数…...
江苏省交通运输厅门户网站建设管理中心/企业网站seo排名
Nacos Naming Configuration Service 一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台 Nacos就是注册中心 配置中心 等价于 Nacos Eureka Config Bus 在docker中安装和配置nacos详细步骤 使用docker部署Nacos 解压和安装 sudo tar -zxvf nacos-server…...
企业网站seo优化交流/百度公司有哪些部门
题库来源:安全生产模拟考试一点通公众号小程序 安全生产模拟考试一点通:茶艺师(初级)考试题根据新茶艺师(初级)考试大纲要求,安全生产模拟考试一点通将茶艺师(初级)模拟…...