SpringBoot测试实践
测试按照粒度可分为3层:
- 单元测试:单元测试(Unit Testing)又称为模块测试 ,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中,一个单元就是单个程序、函数、过程等;对于面向对象编程,最小单元就是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。
- 集成测试:整合测试(Integration Testing),又称组装测试,即对程序模块采用一次性或增值方式组装起来,对系统的接口进行正确性检验的测试工作。整合测试一般在单元测试之后、系统测试之前进行。实践表明,有时模块虽然可以单独工作,但是并不能保证组装起来也可以同时工作。该测试,可以由程序员或是软件品保工程师进行。
- 端到端测试:端到端测试(End To End Testing),又称系统测试。
通常需求开发后需要经过RD单测&自测后进行提测,提测往往需要达到一定的单测/自测代码覆盖率,或者某些基本case通过(冒烟测试),符合提测要求后QA对整体功能进行端到端测试。
完善的测试流程有助于提升代码质量和研发效率,这中间一方面对RD自身的业务素养有要求,另一方面对团队研发流程的规范性有要求。
成熟的研发流程和体系应减少“人性”带来的不稳定性,测试即是应对该不稳定性的有效方法之一。
本文记录了结合SpringBoot进行测试的一些案例,示例代码参见: spring-boot-test-sample
注意区分JUnit4和JUnit5的注解,本文代码基于JUnit4
{: .prompt-warning }
首先我们引入依赖:
<dependencies><!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-junit-jupiter</artifactId><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockito2</artifactId><scope>test</scope></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.6.13</version><type>pom</type><scope>import</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-core</artifactId><version>2.28.2</version><scope>test</scope></dependency><dependency><groupId>org.mockito</groupId><artifactId>mockito-junit-jupiter</artifactId><version>2.28.2</version><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-module-junit4</artifactId><version>2.0.2</version><scope>test</scope></dependency><dependency><groupId>org.powermock</groupId><artifactId>powermock-api-mockito2</artifactId><version>2.0.2</version><scope>test</scope></dependency></dependencies></dependencyManagement>
Mockito & PowerMockito 单元测试
当我们仅仅需要验证代码逻辑,不需要Spring的bean注入时,使用Mockito & PowerMockito来快速测试。
Mockito用于mock对象便于对代码逻辑进行测试&验证,但Mockito mock的方法有限,无法mock final、private、static方法,而PowerMockito框架弥补了这一点。两者可以混合使用。
案例:
@RunWith(PowerMockRunner.class)
// mock static method
@PrepareOnlyThisForTest({SampleUtil.class})
@PowerMockIgnore({"javax.net.ssl.*","javax.management.*", "javax.security.*", "javax.crypto.*"})
public class UnitTest {@Mockprivate SampleRepository sampleRepository;@InjectMocksprivate SampleService sampleService;@BeforeClasspublic static void beforeAll(){System.out.print("\n\n\n++++++++++++++\n\n\n");}@AfterClasspublic static void afterAll(){System.out.print("\n\n\n==============\n\n\n");}@Beforepublic void before(){}@Afterpublic void after(){}@Testpublic void getSamples() throws JSONException {PowerMockito.mockStatic(SampleUtil.class);// 注意所有when内部的方法参数必须用org.mockito.ArgumentMatchers的方法包一层,不能直接传PowerMockito.when(SampleUtil.getSomething(eq("1"))) // 反例:.when(SampleUtil.getSomething("1")) .thenReturn(1L);PowerMockito.when(sampleRepository.selectSamples(argThat(id -> id.equals(1L)))).thenReturn(new ArrayList<>());PowerMockito.when(sampleRepository.selectSamples(argThat(new GreaterOrEqual<>(1L)))).thenReturn(new ArrayList<>());// 这里有any(),anyString()等// 如果参数是String,mock方法传入的是null,则mock不生效,传null需指定为any()Mockito.when(sampleRepository.selectSamples(any())).thenReturn(new ArrayList<>());// verify方法调用次数Mockito.verify(sampleRepository, Mockito.times(1)).selectSamples(any());// Mockito.verify(sampleRepository, Mockito.times(1)).selectSamples(argThat(i->i.equals(1)));// capture参数验证ArgumentCaptor<Long> paramCap = ArgumentCaptor.forClass(Long.class);Mockito.verify(sampleRepository, Mockito.times(1)).selectSamples(paramCap.capture());Assert.assertNotNull(paramCap.getValue());List<Sample> samples = sampleService.listSamples("1");// 如果sample.size()返回Long,需要加一个 sample.size().longValue()方法Assert.assertEquals(0,samples.size());// 比较JSONJSONAssert.assertEquals("{\"a\":1}","{\"a\":1}",false);// 解析JSONAssert.assertEquals(JsonPath.parse("{\"a\":1}").read("$.a").getClass(),Integer.class);}@Testpublic void mockPrivate() {try {Method method = PowerMockito.method(Sample.class, "privateMethodName", Long.class);method.invoke(sampleService, 0L);Assert.fail();} catch (Exception e) {Assert.assertEquals("报错信息", e.getCause().getMessage());}}}
@Mock和@MockBean使用格式:Mockito.when(localVar.method()).thenXxx…
@Spy和@SpyBean使用格式:Mockito.doXxx().when(localVar).method()
Spring 测试
当依赖Spring时,可以利用Spring和PowerMockito一起完成mock和test
案例:
@RunWith(PowerMockRunner.class)
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@PrepareOnlyThisForTest({SampleUtil.class})
@ContextConfiguration(classes = ControllerSliceTestWithPowerMockito.Context.class)
public class ControllerSliceTestWithPowerMockito {// @Import加入需要扫描的Bean// @Configuration配合其他都行,参考@ContextConfiguration注释@Import(SampleController.class)static class Context {}@MockBeanprivate SampleService sampleService;@SpyBeanprivate SampleConverter sampleConverter;@Testpublic void zkSetup() {PowerMockito.mockStatic(SampleUtil.class);PowerMockito.when(SampleUtil.getSomething(eq("a"))).thenReturn(1L);sampleConverter.test();// assert, verify}}
WebMvc 切片测试
- @AutoConfigureWebMvc : Use this if you need to configure the web layer for testing but don’t need to use MockMvc
- @AutoConfigureMockMvc : Use this when you just want to configure MockMvc
- @WebMvcTest : Includes both the @AutoConfigureWebMvc and the @AutoConfigureMockMvc, among other functionality.
三者区别,参考:What’s the difference between @AutoConfigureWebMvc and @AutoConfigureMockMvc?
案例一:
@WebMvcTest(SampleController.class)
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = TestSampleController.TestContext.class)
public class TestSampleController {private static final Logger log = LoggerFactory.getLogger(TestSampleController.class);// 这里填入需要扫描的Bean,这样就不用扫描整个project文件,加快测试速度@Import({SampleController.class, ControllerExceptionAdvice.class})@Configuration // 这里兼容老版本,高版本不用加static class TestContext {}@Autowiredprivate MockMvc mockMvc;@MockBeanprivate SampleService sampleService;// 这里用SpyBean注解:当SampleController中用到了SampleConverter,但是又不需要mock,得用converter原本的逻辑// 或用@MockBean时,在 Mockito.when(...).thenCallRealMethod()就行。@SpyBeanprivate SampleConverter sampleConverter;@Beforepublic void prepareMock() {// 对SampleController中调用了的SampleService的方法进行mockMockito.doNothing().when(sampleService).sampleMethod(Mockito.any());}@Testpublic void shouldReturnSuccess() throws Exception {SampleRequest req = new SampleRequest();req.setA(1L);String bodyJson = JsonUtils.toJson(req);mockMvc.perform(MockMvcRequestBuilders.post("/test").contentType(MediaType.APPLICATION_JSON).content(bodyJson)).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(MockMvcResultMatchers.content().json("{\"success\":true}"));}@Testpublic void shouldReturnErrorMsg() throws Exception {SampleRequest req = new SampleRequest();req.setBString bodyJson = JsonUtils.toJson(req);mockMvc.perform(MockMvcRequestBuilders.post("/test2").contentType(MediaType.APPLICATION_JSON).content(bodyJson)).andExpect(MockMvcResultMatchers.status().isOk()).andExpect(MockMvcResultMatchers.content().contentType(MediaType.APPLICATION_JSON_UTF8)).andExpect(MockMvcResultMatchers.content().json("{\"success\":false,\"errorMsg\":\"错误信息\"}"));}
}
案例二:
@RunWith(PowerMockRunner.class)
@SuppressStaticInitializationFor("com.dianping.cat.Cat")
// mock static method
@PrepareForTest({SampleUtil.class})
// spring bean
@PowerMockRunnerDelegate(SpringJUnit4ClassRunner.class)
@PowerMockIgnore({"javax.net.ssl.*","javax.management.*", "javax.security.*", "javax.crypto.*"})
// @SpringBootTest从当前包向上找@SpringBootConfiguration,或者指定
@SpringBootTest(classes = SpringTestCommonConfig.class)
public class SpringBeanTest {// 这个mock对象会注入Spring容器@MockBeanprivate SampleRepository sampleRepository1;// 真实调用该对象逻辑@SpyBeanprivate SampleRepository sampleRepository2;@Autowiredprivate SampleRepository sampleRepository3;@Autowiredprivate ApplicationContext applicationContext;@Autowiredprivate SampleConfig sampleConfig;@Testpublic void sampleBeanTest() throws JSONException {SampleRepository bean = applicationContext.getBean(SampleRepository.class);Assert.assertEquals(sampleRepository1,bean);}}
此外我们使用h2内存数据库达到对Mapper的测试,也有testcontainers库推出用于测试与外部系统的交互,这里不赘述,详见示例代码
相关文章:

SpringBoot测试实践
测试按照粒度可分为3层: 单元测试:单元测试(Unit Testing)又称为模块测试 ,是针对程序模块(软件设计的最小单位)来进行正确性检验的测试工作。程序单元是应用的最小可测试部件。在过程化编程中…...
Flask-OAuthlib
Flask-OAuthlib库教程 Flask-OAuthlib 是一个为 Flask 应用提供 OAuth1 和 OAuth2 支持的库。它允许开发者轻松地集成第三方 OAuth 服务,或者构建自己的 OAuth 提供者服务。 官方文档链接 Flask-OAuthlib官方文档 架构概述 Flask-OAuthlib 的主要组件包括&…...

树和森林.
目录 一、树 1.1树的存储结构 1.1.1双亲表示法 1.1.2孩子链表 1.1.3孩子兄弟表示法 1.2树与二叉树的转换 1.2.1将树转换成二叉树: 1.2.2将二叉树转换成树 二、森林 2.1森林与二叉树的转换 2.1.1将森林转换成二叉树 2.1.2二叉树转换成森林 三、树和森林的…...

ubuntu下同时安装和使用不同版本的库 librealsense
apt 安装的最新版本在/usr 源码安装的旧版本在/usr/local set(realsense2_DIR /usr/local/) find_package(realsense2 2.50.0 REQUIRED) message( "\n\n ${realsense2_INCLUDE_DIR} ${realsense2_VERSION} RealSense SDK 2.0 is FINDINGING, please install it from…...
openEuler操作系统下静默安装Oracle19c
在openEuler-23.09上安装Oracle19c,创建非容器数据库实例(含静默安装) 操作系统版本 openEuler-23.09-x86_64-dvd.iso ,安装步骤此处省略。。。 最常用且直接的方法来查看openEuler的版本号是查看/etc/os-release文件 [root@openEuler ~]$ cat /etc/os-release NAME="…...
Linux CPU常见命令行详解
在Linux系统中,命令行是管理和监控系统资源的重要工具。特别是当我们需要了解CPU的状态、性能和利用率时,一系列命令行工具就显得尤为重要。本文将详细介绍Linux中与CPU相关的常见命令行工具及其使用方法,帮助大家更好地理解和利用这些工具来…...
防止更新或保存 Laravel 模型
例如,创建模型后,我不希望任何人能够再次更新该记录。相反,它应该被全新的记录覆盖并存档。 这是一个简单的特征,您可以在模型上使用它来禁用更新: trait PreventsUpdating {public static function bootPreventsUpd…...
Cadence:Conformal系列形式验证工具
Conformal 工具最早由Verplex Systems开发。Verplex是一家专注于形式验证工具开发的公司,其核心产品是Conformal等效性检查工具。由于其技术的先进性和市场需求,Verplex的 Conformal工具迅速在半导体行业内获得了认可。 2003 年,Cadence Desi…...

一般人不要学Python?一般人怎么学Python!!
关于“建议一般人真的不要学Python”这一观点,我认为这是一个过于绝对的说法。实际上,Python作为一种流行的编程语言,具有许多优点,适合不同背景和需求的人学习。以下是一些反驳这一观点的理由: 易于学习和理解&#x…...
微服务架构中间件安装部署
微服务架构中间件安装部署 jdk安装 安装包jdk-8u144-linux-x64.tar.gz 先检查系统原版本的jdk并卸载 rpm -qa | grep java 显示信息如下: tzdata-java-2014g-1.el6.noarch java-1.6.0-openjdk-1.6.0.0-11.1.13.4.el6.x86_64 java-1.7.0-openjdk-1.7.0.65-2.5.1.2.…...

车辆数据的提取、定位和融合(其一 共十二篇)
第一篇: System Introduction 第二篇:State of the Art 第三篇:localization 第四篇:Submapping and temporal weighting 第五篇:Mapping of Point-shaped landmark data 第六篇:Clustering of landma…...
Vue3组件通信全解析:利用props、emit、provide/inject跨层级传递数据,expose与ref实现父子组件方法调用
文章目录 一、父组件数据传递N个层级的子组件vue3 provide 与 injectA组件名称 app.vueB组件名称 provideB.vueC组件名称 provideCSetup.vue 二、使用v-model指令实现父子组件的双向绑定父组件名称 app.vue子组件名称 v-modelSetup.vue 三、父组件props向子组件传值子组件 prop…...

华为---OSPF被动接口配置(四)
9.4 OSPF被动接口配置 9.4.1 原理概述 OSPF被动接口也称抑制接口,成为被动接口后,将不会接收和发送OSPF报文。如果要使OSPF路由信息不被某一网络中的路由器获得且使本地路由器不接收网络中其他路由器发布的路由更新信息,即已运行在OSPF协议…...
前端将Markdown文本转换为富文本显示/编辑,并保存为word文件
参考:https://www.wangeditor.com/ https://blog.csdn.net/weixin_43797577/article/details/138854324 插件: markdown-it traptitech/markdown-it-katex markdown-it-link-attributes highlight.js wangeditor/editor wangeditor/editor-for-vue html…...
git-shortlog详解
作用 git-shortlog - Summarize git log output 语法 git shortlog [<options>] [<revision-range>] [[--] <path>…] git log --prettyshort | git shortlog [<options>] 功能描述 Summarizes git log output in a format suitable for inclus…...

通过MATLAB实现PID控制器,积分分离控制器以及滑模控制器
目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 5.完整工程文件 1.课题概述 通过MATLAB实现PID控制器,积分分离控制器以及滑模控制器。通过对比三个算法可知,采用滑模控制算法,其具有最快的收敛性能,较强的鲁棒性&…...

Node.js 渲染三维模型并导出为图片
Node.js 渲染三维模型并导出为图片 1. 前言 本文将介绍如何在 Node.js 中使用 Three.js 进行 3D 模型渲染。通过结合 gl 和 canvas 这两个主要依赖库,我们能够在服务器端实现高效的 3D 渲染。这个方法解决了在服务器端生成和处理 3D 图形的需求,使得可…...

Win11下安装VS2022失败的解决办法
前几天我把我的HP Z840的操作系统换成了Win11,在重装VS2022时遇到了麻烦,提示无法安装 Microsoft.VisualStudio.Devenv.Msi。 查看安装日志提示:Could not write value devenv.exe to key \SOFTWARE\Microsoft\Internet Explorer\Main\Featur…...

动态规划:基本概念
Dynamic Programming 动态规划(Dynamic Programming, DP) 是一种算法设计技巧,通常用来解决具有重叠子问题和最优子结构性质的问题。它通过将问题分解为更小的子问题,逐步解决这些子问题并将结果存储起来,以避免重复计…...
小山菌_代码随想录算法训练营第二十九天| 455. 分发饼干 、376. 摆动序列、53. 最大子序和
455. 分发饼干 文档讲解:代码随想录.分发饼干 视频讲解:贪心算法,你想先喂哪个小孩?| LeetCode:455.分发饼干 状态:已完成 代码实现 class Solution { public:int findContentChildren(vector<int>&…...

铭豹扩展坞 USB转网口 突然无法识别解决方法
当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…...

测试微信模版消息推送
进入“开发接口管理”--“公众平台测试账号”,无需申请公众账号、可在测试账号中体验并测试微信公众平台所有高级接口。 获取access_token: 自定义模版消息: 关注测试号:扫二维码关注测试号。 发送模版消息: import requests da…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

python/java环境配置
环境变量放一起 python: 1.首先下载Python Python下载地址:Download Python | Python.org downloads ---windows -- 64 2.安装Python 下面两个,然后自定义,全选 可以把前4个选上 3.环境配置 1)搜高级系统设置 2…...

从零实现STL哈希容器:unordered_map/unordered_set封装详解
本篇文章是对C学习的STL哈希容器自主实现部分的学习分享 希望也能为你带来些帮助~ 那咱们废话不多说,直接开始吧! 一、源码结构分析 1. SGISTL30实现剖析 // hash_set核心结构 template <class Value, class HashFcn, ...> class hash_set {ty…...
大语言模型(LLM)中的KV缓存压缩与动态稀疏注意力机制设计
随着大语言模型(LLM)参数规模的增长,推理阶段的内存占用和计算复杂度成为核心挑战。传统注意力机制的计算复杂度随序列长度呈二次方增长,而KV缓存的内存消耗可能高达数十GB(例如Llama2-7B处理100K token时需50GB内存&a…...
08. C#入门系列【类的基本概念】:开启编程世界的奇妙冒险
C#入门系列【类的基本概念】:开启编程世界的奇妙冒险 嘿,各位编程小白探险家!欢迎来到 C# 的奇幻大陆!今天咱们要深入探索这片大陆上至关重要的 “建筑”—— 类!别害怕,跟着我,保准让你轻松搞…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...
Spring AI Chat Memory 实战指南:Local 与 JDBC 存储集成
一个面向 Java 开发者的 Sring-Ai 示例工程项目,该项目是一个 Spring AI 快速入门的样例工程项目,旨在通过一些小的案例展示 Spring AI 框架的核心功能和使用方法。 项目采用模块化设计,每个模块都专注于特定的功能领域,便于学习和…...

【Veristand】Veristand环境安装教程-Linux RT / Windows
首先声明,此教程是针对Simulink编译模型并导入Veristand中编写的,同时需要注意的是老用户编译可能用的是Veristand Model Framework,那个是历史版本,且NI不会再维护,新版本编译支持为VeriStand Model Generation Suppo…...