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

Springboot教程(五)——单元测试

idea中一般使用JUnit进行单元测试 

基本使用

我们可以在idea的test文件夹下的XXXXApplicationTests内进行单元测试:

可以在@Test标注的方法上写测试代码: 

@SpringBootTest
class C0101ApplicationTests {@Testfun contextLoads() {println("Hello World")}}

我们也可以写多个测试方法:

@SpringBootTest
class C0101ApplicationTests {@Testfun test1() {println("test1")}@Testfun test2() {println("test2")}}

我们也可以在测试类内使用@Autowired注解,如我们可以自动注入写好的服务:

@Autowired
lateinit var testService: TestService

我们来举个例子,先创建一个服务:

package com.example.c0101.serviceimport org.springframework.stereotype.Service@Service
class TestService {fun check(username: String, password: String): Boolean{return username == "admin" && password == "123456"}
}

然后在测试类内使用Autowired自动注入服务,并进行测试:

package com.example.c0101import com.example.c0101.service.TestService
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest@SpringBootTest
class C0101ApplicationTests {@Autowiredlateinit var testService: TestService@Testfun test() {println(testService.check("111", "123"))println(testService.check("admin", "123456"))}}

控制台输出:

...
false
true
...

测前准备和测后收尾

我们可以用以下注解实现测前准备和测后收尾:

  • @BeforeEach:在每一个测试方法执行前执行,其标注的方法可以传入一个TestInfo类型的参数,为当前测试信息的对象
  • @AfterEach:在每一个测试方法执行后执行,其标注的方法可以传入一个TestInfo类型的参数,为当前测试信息的对象
  • @BeforeAll:在所有测试方法执行前只执行一次
  • @AfterAll:在所有测试方法执行后只执行一次

另外,@BeforeAll和@AfterAll标注的方法需要为静态,在kotlin中需要放在companion object的代码块下,并用@JvmStatic注解标注


以下代码展示了这些注解的用法:

package com.example.c0101import com.example.c0101.service.TestService
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.AfterEach
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.BeforeEach
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.TestInfo
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest@SpringBootTest
class C0101ApplicationTests {@BeforeEachfun beforeEach(info: TestInfo){println("即将进入测试方法:${info.testMethod.get()}")}@AfterEachfun afterEach(info: TestInfo){println("已经离开测试方法:${info.testMethod.get()}")}companion object {@JvmStatic@BeforeAllfun beforeAll(){println("即将进入测试")}@JvmStatic@AfterAllfun afterAll(){println("测试已完成")}}@Testfun test() {println("Hello World")}}

控制台输出:

...
即将进入测试
...
即将进入测试方法:public void com.example.c0101.C0101ApplicationTests.test()
Hello World
已经离开测试方法:public void com.example.c0101.C0101ApplicationTests.test()
测试已完成
...

设置测试用例

要想设置测试用例,需要使用@ParameterizedTest注解,该注解可以传入name参数,可以为测试方法起别名。另外,可以用@ValueSource注解设置参数源:

@ParameterizedTest
@ValueSource(ints = [1, 2, 3, 4, 5])
fun test(num: Int) {println("$num")
}

注意,被@ParameterizedTest注解标注的测试方法就不需要用@Test注解标注了

JUnit会将所有的测试用例都测试一遍,因此这个测试方法会被执行5次:

...
1
2
3
4
5
...

我们也可以用@MethodSource注解设置测试用例,它将会把一个静态方法的返回值作为测试用例:

companion object{@JvmStaticfun getInt(): Stream<Int>{return Stream.of(1, 2, 3, 4, 5)}
}@ParameterizedTest
@MethodSource("getInt")
fun test(num: Int) {println("$num")
}

注意:这里面的Stream是java.util.stream下的Stream类

使用这种方法,我们就可以传入多个参数了:

companion object{@JvmStaticfun getProducts(): Stream<Arguments>{return Stream.of(Arguments.of("鼠标", 49.9),Arguments.of("键盘", 59.9))}
}@ParameterizedTest
@MethodSource("getProducts")
fun test(name: String, price: Double) {println("$name 卖 $price 元")
}

输出:

...
鼠标 卖 49.9 元
键盘 卖 59.9 元
...

断言

测试人员可以断言一件事是真的,如果这件事不是真的,则测试失败

JUnit提供了Assertions类,用于进行断言:

@Test
fun test() {Assertions.assertTrue(1 > 2)
}

这段代码断言了1>2是真的,如果不是真的(当然不是真的),则测试失败:

idea提示测试失败
标题

断言的应用

还记得之前的测试服务的代码吗,这个服务在传入用户名为admin且密码为123456后应该返回true,如果返回的不是true,说明这个服务写错了;同理,如果传入的用户名不是admin或密码不是123456,则应该返回false,如果返回的不是false,同样说明这个服务写错了。我们可以用断言来测试这个功能:

@Autowired
lateinit var testService: TestService@Test
fun test() {Assertions.assertTrue(testService.check("admin", "123456"))Assertions.assertTrue(!testService.check("aaa", "123"))
}

可以看到,测试通过,说明check没有写错

模拟Servlet对象

如果要测试controller等需要使用Servlet对象(例如HttpServletRequest)的方法,就需要模拟Servlet对象,我们可以在测试类自动注入以下对象模拟:

@Autowired
lateinit var mockHttpServletRequest: MockHttpServletRequest
@Autowired
lateinit var mockHttpServletResponse: MockHttpServletResponse
@Autowired
lateinit var mockHttpSession: MockHttpSession

这些代码在idea里可能会报错,不过没有关系

另外,在测试contoller时,同样需要@Autowired:

@Autowired
lateinit var controller: TestController

我们来举个模拟Servlet的例子:

先创建一个controller:

package com.example.c0101.controllerimport jakarta.servlet.http.HttpServletRequest
import jakarta.servlet.http.HttpServletResponse
import jakarta.servlet.http.HttpSession
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RestController@RestController
class TestController {@RequestMappingfun index(request: HttpServletRequest, response: HttpServletResponse, session: HttpSession): String{response.status = 404return "Hello World"}}

当访问主页时,会设置状态码为404,并返回Hello World

接下来编写测试类:

package com.example.c0101import com.example.c0101.controller.TestController
import com.example.c0101.service.TestService
import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.Test
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.boot.test.context.SpringBootTest
import org.springframework.mock.web.MockHttpServletRequest
import org.springframework.mock.web.MockHttpServletResponse
import org.springframework.mock.web.MockHttpSession@SpringBootTest
class C0101ApplicationTests {@Autowiredlateinit var controller: TestController@Autowiredlateinit var mockHttpServletRequest: MockHttpServletRequest@Autowiredlateinit var mockHttpServletResponse: MockHttpServletResponse@Autowiredlateinit var mockHttpSession: MockHttpSession@Testfun test() {val res = controller.index(mockHttpServletRequest, mockHttpServletResponse, mockHttpSession)Assertions.assertTrue(mockHttpServletResponse.status == 404)println(res)}}

测试类中,我们断言了状态码一定是404,并输出了返回结果

控制台输出如下:

...
Hello World
...

相关文章:

Springboot教程(五)——单元测试

idea中一般使用JUnit进行单元测试 基本使用 我们可以在idea的test文件夹下的XXXXApplicationTests内进行单元测试&#xff1a; 可以在Test标注的方法上写测试代码&#xff1a; SpringBootTest class C0101ApplicationTests {Testfun contextLoads() {println("Hello …...

【Kotlin】函数

1 常规函数 1.1 无参函数 fun main() {myFun() }fun myFun() {println("myFun") // 打印: myFun } 1.2 有参函数 1&#xff09;常规调用 fun main() {myFun("myFun") // 打印: myFun }fun myFun(str: String) {println(str) } 2&#xff09;形参指定默…...

Unity生命周期函数解析

本文由 简悦 SimpRead 转码&#xff0c; 原文地址 mp.weixin.qq.com Unity生命周期函数解析 Unity 生命周期函数是在游戏对象的不同阶段被调用的方法&#xff0c;通过这些函数&#xff0c;我们可以在不同的时刻执行特定的代码。在这篇文章中&#xff0c;我们将一步步解析 Unit…...

【Qt】QTextEdit/QPlainTextEdit 实现 Tab 键多行缩进与反缩进

【Qt】QTextEdit/QPlainTextEdit 实现 Tab 键多行缩进与反缩进 文章目录 I - 主要原理II - 代码实现2.1 - 自定义类2.2 - 实现 Tab 缩进2.3 - 实现反缩进 III - 参考链接 I - 主要原理 由于 QTextEdit 和 QPlainTextEdit &#xff0c;都无法实现多行选中缩进与反缩进&#xff…...

C++缺陷与思考

数组隐式转换为指针 size_t func(int a[10]) {return sizeof(a); }int a[100]; func(a); // 指针大小 sizeof(a); // 数组大小函数的参数看似是一个数组形式&#xff0c;但事实上他已经退化为指针了&#xff0c;也就是等价于size_t func(int* a)&#xff0c;而数组作为参数传…...

无公网ip环境使用DS file软件远程访问内网群晖NAS中储存的文件

文章目录 1. 群晖安装Cpolar2. 创建TCP公网地址3. 远程访问群晖文件4. 固定TCP公网地址5. 固定TCP地址连接 DS file 是一个由群晖公司开发的文件管理应用程序&#xff0c;主要用于浏览、访问和管理存储在群晖NAS&#xff08;网络附加存储&#xff09;中的文件。这个应用程序具有…...

软件工程基础

本博客地址&#xff1a;https://security.blog.csdn.net/article/details/136446772 一. 软件工程 1、软件危机。具体表现为&#xff1a;软件开发进度难以预测、软件开发成本难以控制、软件功能难以满足用户期望、软件质量无法保证、软件难以维护和软件缺少适当的文档资料。 …...

alzet供应商你值得拥有

在20世纪70年代&#xff0c;ALZE公司研发出来一款巧妙的药物输送装置——Alzet osmotic pump。这款产品如胶囊般精致小巧&#xff0c;它既有胶囊的外表&#xff0c;也具有胶囊的作用。在Alzet osmotic pump中藏有可以装配药物溶液的空间。此款胶囊泵如同一个小投递员&#xff0…...

x86中的TSS与任务切换

前言 今天在学习《深入理解Linux内核》的时候&#xff0c;发现出现了一个新的名词TSS&#xff08;Task-State Segment&#xff09;&#xff0c;这还是我第一次了解到原来x86提供了硬件级别的任务切换功能&#xff0c;之前以为任务切换都是操作系统实现的来着&#xff0c;这里也…...

打造去中心化透明储蓄罐:Solidity智能合约的又一实践

一、案例背景 传统的储蓄罐通常是由个人或家庭使用&#xff0c;用于存放硬币或小额纸币。然而&#xff0c;这样的储蓄罐缺乏透明性&#xff0c;用户无法实时了解储蓄情况&#xff0c;也无法确保资金的安全性。 通过Solidity智能合约&#xff0c;我们可以构建一个去中心化…...

Java Mybatis数据库面试题

Java Mybatis数据库面试题 前言1、什么是 Mybatis&#xff1f;2、Mybaits 的优缺点&#xff1a;3、SQL 注入如何防止&#xff1f;4、MyBatis 框架适用场合&#xff1a;5、MyBatis 与 Hibernate 有哪些不同&#xff1f;6、#{}和${}的区别是什么&#xff1f;7、当表中的字段名和实…...

LeetCode-第14题-最长公共前缀

1.题目描述 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 2.样例描述 3.思路描述 按字符串数组每个数组的长度&#xff0c;将字符串数组从小到大排序&#xff1b;他们的公共前缀一定小于或等于最长元素长度…...

TCP/UDP模型:2024/2/29

作业1&#xff1a;TCP模型 服务器端&#xff1a; #include <myhead.h> #define SER_IP "192.168.199.129" #define SER_PORT 8899int main(int argc, const char *argv[]) {//1.创建用于连接的套接字文件int sfdsocket(AF_INET,SOCK_STREAM,0);if(sfd-1){per…...

微信如何设置自动回复消息,提升沟通效率的?

在日常微信聊天过程中&#xff0c;我们可能会频繁遇到相同问题的客户提问&#xff0c;特别是对于从事销售工作的朋友们而言&#xff0c;客户添加好友后的第一句话常常为“在吗”或“你好”。当我们拥有大量好友&#xff0c;手动逐一回复可能会耗费大量时间。因此&#xff0c;自…...

PCIE的BAR空间

1.PCIe 简介 PCIe&#xff08;Peripheral Component Interconnect Express&#xff09;是一种高速 串行计算机扩展总线标准&#xff0c;主要用于连接主板上的中央处理器&#xff08;CPU&#xff09;和 各种外部设备&#xff0c;如显卡、声卡、硬盘等。PCIe 总线取代了传统的 PC…...

11.互信息-机器学习模型性能的常用的评估指标

互信息&#xff08;Mutual Information&#xff09;是机器学习中常用的一种评估指标&#xff0c;特别是在无监督学习和聚类分析中。它用于衡量两个随机变量之间的相关性或相似性。 定义 给定两个随机变量X和Y&#xff0c;它们的互信息I(X;Y)定义如下&#xff1a; 其中&…...

SpringCloud(18)之Sleuth +Zipkin链路追踪

一、Zipkin介绍 Zipkin是一个开放源代码分布式的跟踪系统&#xff0c;它可以帮助收集服务的时间数据&#xff0c;以解决微服务架构中的延迟问 题&#xff0c;包括数据的收集、存储、查找和展现。每个服务向zipkin报告计时数据&#xff0c;zipkin会根据调用关系通 过Zipkin UI…...

GVA快速使用

1. clone 代码&#xff0c; 使用goland打开Server目录&#xff0c; 使用vsc打开前端web目录&#xff0c;运行后端&#xff0c;前端 gin-vue-admin后台管理系统 - 知乎 (zhihu.com) 2.了解端口配置 参考&#xff0c; 基于Go的后台管理框架Gin-vue-admin_go vue admin-CSDN博客…...

Linux文本处理三剑客:awk(内置函数详解笔记)

Linux系统中&#xff0c;AWK 是一个非常强大的文本处理工具&#xff0c;它的内置函数使得对文本数据进行处理更加高效和便捷。 本文将介绍 AWK 内置函数的几种主要类型&#xff1a; 算数函数字符串函数时间函数位操作函数其他常用函数 我们将使用一个示例文本文件来演示这些函…...

C++调用lua函数

C 调用Lua全局变量(普通) lua_getglobal(lua, "width");int width lua_tointeger(lua,-1);lua_pop(lua,1);std::cout << width << std::endl;lua_close(lua); 这几行代码要放到lua_pcall(lua, 0,0,0);之后才可以. C给lua传递变量 lua_pushstring(lua, …...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

Cinnamon修改面板小工具图标

Cinnamon开始菜单-CSDN博客 设置模块都是做好的&#xff0c;比GNOME简单得多&#xff01; 在 applet.js 里增加 const Settings imports.ui.settings;this.settings new Settings.AppletSettings(this, HTYMenusonichy, instance_id); this.settings.bind(menu-icon, menu…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比

目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec&#xff1f; IPsec VPN 5.1 IPsec传输模式&#xff08;Transport Mode&#xff09; 5.2 IPsec隧道模式&#xff08;Tunne…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

【生成模型】视频生成论文调研

工作清单 上游应用方向&#xff1a;控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...

零知开源——STM32F103RBT6驱动 ICM20948 九轴传感器及 vofa + 上位机可视化教程

STM32F1 本教程使用零知标准板&#xff08;STM32F103RBT6&#xff09;通过I2C驱动ICM20948九轴传感器&#xff0c;实现姿态解算&#xff0c;并通过串口将数据实时发送至VOFA上位机进行3D可视化。代码基于开源库修改优化&#xff0c;适合嵌入式及物联网开发者。在基础驱动上新增…...

Vue 模板语句的数据来源

&#x1f9e9; Vue 模板语句的数据来源&#xff1a;全方位解析 Vue 模板&#xff08;<template> 部分&#xff09;中的表达式、指令绑定&#xff08;如 v-bind, v-on&#xff09;和插值&#xff08;{{ }}&#xff09;都在一个特定的作用域内求值。这个作用域由当前 组件…...