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>&…...

工业安全零事故的智能守护者:一体化AI智能安防平台
前言: 通过AI视觉技术,为船厂提供全面的安全监控解决方案,涵盖交通违规检测、起重机轨道安全、非法入侵检测、盗窃防范、安全规范执行监控等多个方面,能够实现对应负责人反馈机制,并最终实现数据的统计报表。提升船厂…...
镜像里切换为普通用户
如果你登录远程虚拟机默认就是 root 用户,但你不希望用 root 权限运行 ns-3(这是对的,ns3 工具会拒绝 root),你可以按以下方法创建一个 非 root 用户账号 并切换到它运行 ns-3。 一次性解决方案:创建非 roo…...

C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...

听写流程自动化实践,轻量级教育辅助
随着智能教育工具的发展,越来越多的传统学习方式正在被数字化、自动化所优化。听写作为语文、英语等学科中重要的基础训练形式,也迎来了更高效的解决方案。 这是一款轻量但功能强大的听写辅助工具。它是基于本地词库与可选在线语音引擎构建,…...
PostgreSQL——环境搭建
一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在࿰…...

解析奥地利 XARION激光超声检测系统:无膜光学麦克风 + 无耦合剂的技术协同优势及多元应用
在工业制造领域,无损检测(NDT)的精度与效率直接影响产品质量与生产安全。奥地利 XARION开发的激光超声精密检测系统,以非接触式光学麦克风技术为核心,打破传统检测瓶颈,为半导体、航空航天、汽车制造等行业提供了高灵敏…...
Qt 事件处理中 return 的深入解析
Qt 事件处理中 return 的深入解析 在 Qt 事件处理中,return 语句的使用是另一个关键概念,它与 event->accept()/event->ignore() 密切相关但作用不同。让我们详细分析一下它们之间的关系和工作原理。 核心区别:不同层级的事件处理 方…...
微服务通信安全:深入解析mTLS的原理与实践
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、引言:微服务时代的通信安全挑战 随着云原生和微服务架构的普及,服务间的通信安全成为系统设计的核心议题。传统的单体架构中&…...

C++_哈希表
本篇文章是对C学习的哈希表部分的学习分享 相信一定会对你有所帮助~ 那咱们废话不多说,直接开始吧! 一、基础概念 1. 哈希核心思想: 哈希函数的作用:通过此函数建立一个Key与存储位置之间的映射关系。理想目标:实现…...