使用Springboot做测试的步骤详解

  •  📢专注于分享软件测试干货内容,欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
  • 📢交流讨论:欢迎加入我们一起学习!
  • 📢资源分享:耗时200+小时精选的「软件测试」资料包
  • 📢 软件测试学习教程推荐:火遍全网的《软件测试》教程


介绍


在毕业后的第一家公司和第二家公司,基本上都在做代码迁移重构(切换语言)的工作,第一家是php转java,现在在的部门是clojure转java。

因为重构是一个人能完成的工作,不需要和客户端对接,所以是能够全身心投入的一个过程。在熟悉既有的业务以及与外部交互的入参、出参后,就可以对任意逻辑进行迁移以及重新设计了。

对于重构,我很在意自己的代码质量,以及日常的改动影响了线上了业务。

如果通过本地启动项目,或者在测试环境再去验证业务功能,这些步骤无疑是很浪费时间的。所以通过有意义的测试用例,每次合并代码前,进行自动化的测试就很放心。

以前写测试用例时,比较死板。总是看着代码逻辑去写测试用例,想着通过什么数据能够覆盖所有分支,导致一有改动就会去改测试用例。

时间久了,就会觉得测试用例很浪费时间,写着没什么意思。但是自从接触了Spock(BDD)后,也走出了通过数据来覆盖分支的误区。我们开发的业务,不是一成不变的,不可能每个方法都能100%覆盖并且永远不改动,只要能够通过测试用例覆盖大多数场景即可。

通过@SpringbootTest(classes=XxxApplication.class)的方式来进行测试,显得很笨重,会增加测试用例运行时间。

通过对Spring Test的理解,我对日常用到的测试工具进行了精简和封装。

包括:

最小化的spring容器环境

自动根据测试方法加载sql

通过ORM实体类生成truncate sql的注解

按需加载myabtis-plus-mapper的注解

断言工具类的封装

测试支持

介绍

测试环境支持了junit5、spock两种测试框架,基于springboot的集成测试,抽象了相关的注解和插件。

不同于常见的用法,虽然使用了@SpringBootTest,但是去掉了自动装配(不会扫描哪些AutoConfiguration),也不会扫描当前项目里面的所有bean。

bean的加载是依据最小化原则,需要测哪些bean就用哪些bean,不用的就不加载

@SpringBootTest-冷知识

使用@SpringBootTest时,每次都会启动完整的容器。

当我们依赖的AutoConfiguration和项目里面的bean比较多时,启动一个容器的成本是很大的。SpringTest在这块做了优化,会对容器进行缓存,涉及的类包含DefaultCacheAwareContextLoaderDelegate和MergedContextConfiguration等,判断是否走缓存的依据在MergedContextConfiguration的hashcode里面,代码如下:
 

 /*** Generate a unique hash code for all properties of this* {@code MergedContextConfiguration} excluding the* {@linkplain #getTestClass() test class}.*/@Overridepublic int hashCode() {int result = Arrays.hashCode(this.locations);result = 31 * result + Arrays.hashCode(this.classes);result = 31 * result + this.contextInitializerClasses.hashCode();result = 31 * result + Arrays.hashCode(this.activeProfiles);result = 31 * result + Arrays.hashCode(this.propertySourceLocations);result = 31 * result + Arrays.hashCode(this.propertySourceProperties);result = 31 * result + this.contextCustomizers.hashCode();result = 31 * result + (this.parent != null ? this.parent.hashCode() : 0);result = 31 * result + nullSafeClassName(this.contextLoader).hashCode();return result;}

如果Profile,@SpringBootTest上面的Application,context相关的初始化器、自定义的PropertySource等有变化,就会重新创建一个容器。

上面这些都是改动比较少,this.classes(容器中所有对象的class)是最容易变动的,在测试中我们难免会MOCK对象,一旦两个测试用例MOCK的对象不同或者一个有一个没有,那么他们的容器也是分别启动的。

而且如果服用容器,那就代表 @MockBean @SpyBean的对象都是复用,不同的测试用例对mock对象的mock逻辑都会叠加,单个单个跑没问题,但是当执行所有case时,就会失败部分用例。
 

注解

pers.zengsx.toolkit.test.sdk.base.LiteTest

定义了一个最小化的容器(所以这也是一个一次性容器,每个测试用例类都是单独的容器,保证了每个测试用例类都是独立环境),并且关闭了自动加载bean。已包括的能力有插件(后面专门讲):

自动读取加载SqlFile

根据实体生成Truncate Table SQL

使用的服务一般都有自定义的组件,比如DAO、CACHE、LOCK等。在test-example的项目,对LiteTest进行了再次封装了,增加了plugins支持,详情见后文。

示例如下:
 

 package pers.zengsx.toolkit.test.example;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.context.annotation.Import;import pers.zengsx.toolkit.test.example.custom.ExampleLiteTest;import pers.zengsx.toolkit.test.example.custom.Plugins;import pers.zengsx.toolkit.test.example.dao.IUserDAO;import pers.zengsx.toolkit.test.example.dao.impl.UserDAOImpl;import pers.zengsx.toolkit.test.example.dao.mappers.IUserMapper;import pers.zengsx.toolkit.test.example.model.User;import pers.zengsx.toolkit.test.sdk.plugin.truncate_table.TruncateTable;import pers.zengsx.toolkit.test.sdk.registrar.mybatis.LoadMybatisMapper;import pers.zengsx.toolkit.test.sdk.utils.AssertHelper;@ExampleLiteTest(Plugins.DAO)@Import(UserDAOImpl.class)@LoadMybatisMapper(classes = IUserMapper.class)public class FirstExampleTest {@AutowiredIUserDAO userDAO;@Test@TruncateTable(mpEntityClasses = User.class)void test() {AssertHelper.assertEqualsByJson("{\"id\":1,\"name\":\"mr.zeng\",\"nickname\":\"seven\"}", userDAO.getById(1));AssertHelper.assertEqualsByJson(null, userDAO.getById(2));}}

 

上面的示例启用了DAO组件,sql初始化数据是通过AUTO_LOAD_SQL组件自动加载的,测试了mybatis的查询。

pers.zengsx.toolkit.test.sdk.base.LiteWebTest

在LiteTest的基础上增加了MockMvc的支持

pers.zengsx.toolkit.test.example.custom.ExampleLiteWebTest 在 LiteWebTest 的基础上增加了项目的自定义支持。

示例如下:
 

 package pers.zengsx.toolkit.test.example;import lombok.SneakyThrows;import org.junit.jupiter.api.Test;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.boot.test.mock.mockito.MockBean;import org.springframework.context.annotation.Import;import org.springframework.test.web.servlet.MockMvc;import org.springframework.web.util.NestedServletException;import pers.zengsx.toolkit.test.example.controller.TestController;import pers.zengsx.toolkit.test.example.custom.ExampleLiteWebTest;import pers.zengsx.toolkit.test.example.dao.IUserDAO;import static org.hamcrest.Matchers.containsString;import static org.junit.jupiter.api.Assertions.assertThrows;import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;@ExampleLiteWebTest@Import(TestController.class)public class FirstWebExampleTest {@AutowiredMockMvc mockMvc;@MockBeanIUserDAO userDAO;@Test@SneakyThrowsvoid test() {mockMvc.perform(get("/test/HelloWorld")).andDo(print()).andExpect(status().isOk()).andExpect(content().string(containsString("HelloWorld")));assertThrows(NestedServletException.class, () -> mockMvc.perform(get("/test/throwError")));}}

 

pers.zengsx.toolkit.test.sdk.registrar.mybatis.LoadMybatisMapper

如果要使用 IXXXMapper(mybatis-plus的mapper),不能直接通过 @Import(IXXXMapper.class)进行导入(即使加了@Mapper注解),这里做了特殊处理才能支持。

pers.zengsx.toolkit.test.sdk.plugin.truncate_table.TruncateTable

当测试用例上用了 @TruncateTable(mpEntityClasses=XxxAutogenMybatisPO.class), 会去读取XxxAutogenMybatisPO.class上面的@TableName注解,生成truncate table sql并执行。
 

插件

TruncateTable-ORM Entity Truncate

描述同上 @TruncateTable。

AutoLoadSqlFile-自动加载SQL FIle(目前spock test case不支持)

在执行测试用例之前会根据当前的类名和方法名去 resources/it-sql下面去找 类名_方法名.sql 并加载到数据库中。

Spock

介绍

Spock编写测试用例在语法上比Junit方便许多。除了个别注解有差异/语法差异,其他的都差不多。

下面仅举例两个,其他的自行探索。示例如下(最喜欢的就是支持跨行字符串,可以直接复制json来比较object):
 

 package pers.zengsx.toolkit.test.exampleimport org.springframework.beans.factory.annotation.Autowiredimport org.springframework.context.annotation.Importimport org.springframework.test.context.jdbc.Sqlimport pers.zengsx.toolkit.test.example.custom.ExampleLiteTestimport pers.zengsx.toolkit.test.example.custom.Pluginsimport pers.zengsx.toolkit.test.example.dao.IUserDAOimport pers.zengsx.toolkit.test.example.dao.impl.UserDAOImplimport pers.zengsx.toolkit.test.example.dao.mappers.IUserMapperimport pers.zengsx.toolkit.test.example.model.Userimport pers.zengsx.toolkit.test.sdk.plugin.truncate_table.TruncateTableimport pers.zengsx.toolkit.test.sdk.registrar.mybatis.LoadMybatisMapperimport pers.zengsx.toolkit.test.sdk.utils.AssertHelperimport spock.lang.Specificationimport spock.lang.Unroll@Unroll@ExampleLiteTest(Plugins.DAO)@Import(UserDAOImpl.class)@LoadMybatisMapper(classes = IUserMapper.class)class FirstExampleGTest extends Specification {@AutowiredIUserDAO userDAO@Sql(scripts = "classpath:it-sql/FirstExampleTest_test.sql")@TruncateTable(mpEntityClasses = User.class)def "test"() {expect:AssertHelper.assertEqualsByJson('''{"id":1,"name":"mr.zeng","nickname":"seven"}''', userDAO.getById(1));AssertHelper.assertEqualsByJson(null, userDAO.getById(2));}}

github测试工具包地址:


最后我邀请你进入我们的软件测试学习交流群, 大家可以一起探讨交流软件测试,共同学习软件测试技术、面试等软件测试方方面面,还会有免费直播课,收获更多测试技巧,我们一起进阶Python自动化测试/测试开发,走向高薪之路

感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的,虽然不是什么很值钱的东西,如果你用得到的话可以直接拿走:

这些资料,对于从事【软件测试】的朋友来说应该是最全面最完整的备战仓库,这个仓库也陪伴我走过了最艰难的路程,希望也能帮助到你!

关注【公众号:豆子熊】免费领取!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/295349.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

CV算法面试题学习

本文记录了CV算法题的学习。 CV算法面试题学习 点在多边形内(point in polygon)高斯滤波器 点在多边形内(point in polygon) 参考自文章1,其提供的代码没有考虑一些特殊情况,所以做了改进。 做法&#xff…

Zookeeper-应用实战

Zookeeper Java客户端实战 ZooKeeper应用的开发主要通过Java客户端API去连接和操作ZooKeeper集群。 ZooKeeper官方的Java客户端API。 第三方的Java客户端API,比如Curator。 ZooKeeper官方的客户端API提供了基本的操作:创建会话、创建节点、读取节点、更新数据、…

【Unity基础】9.地形系统Terrain

【Unity基础】9.地形系统Terrain 大家好,我是Lampard~~ 欢迎来到Unity基础系列博客,所学知识来自B站阿发老师~感谢 (一)地形编辑器Terrain (1)创建地形 游戏场景中大多数的山川河流地表地貌都是基…

16-高并发-队列术

队列,在数据结构中是一种线性表,从一端插入数据,然后从另一端删除数据。 在我们的系统中,不是所有的处理都必须实时处理,不是所有的请求都必须实时反馈结果给用户,不是所有的请求都必须100%一次性处理成功…

数据结构(超详细讲解!!)第二十八节 排序

1.排序的几个基本概念 排序就是将一个数据元素(或记录)的任意序列,重新排列成一个按关键字有序的序列。 数据表(Data List) :待排序的数据对象的有限集合。 关键字(Key):数据元素(或记录)中…

GPT2代码运行,个人文本生成助手,不依赖OpenAI API调用

0.前言: 感觉GPT很好玩,所以想要有个自己搭建GPT的写法,不依赖于OpenAI,需要翻墙太麻烦了,近日日本已经结合GPT4和机器,可以让他吓人,做出丰富的表情,如果自己训练的话,会塑造出什么样的机器人尚未可知…抱着好奇的心态,去github openai下载了个gpt2的模型来玩玩(其中遇到了许多…

HackTheBox - Medium - Linux - Sandworm (我的创作纪念日

Sandworm Sandworm 是一台中等难度的 Linux 机器,它托管了一个具有“PGP”验证服务的 Web 应用程序,该服务容易受到服务器端模板注入 (SSTI) 的攻击,导致“Firejail”监狱内的远程代码执行 (RCE&#xff0…

C++内存管理和模板初阶

C/C内存分布 请看代码: int globalVar 1; static int staticGlobalVar 1; void Test() {static int staticVar 1;int localVar 1;int num1[10] { 1, 2, 3, 4 };char char2[] "abcd";const char* pChar3 "abcd";int* ptr1 (int*)mallo…

【STM32】I2C通信

基本的任务是:通过通信线,实现单片机读写外挂模块寄存器的功能。其中至少要实现在指定位置写寄存器和在指定的位置读寄存器这两个功能。 异步时序的优点:省一根时钟线,节约资源;缺点:对事件要求严格&#…

【XR806开发板试用】通过http请求从心知天气网获取天气预报信息

1. 开发环境搭建 本次评测开发环境搭建在windows11的WSL2的Ubuntu20.04中,关于windows安装WSL2可以参考文章: Windows下安装Linux(Ubuntu20.04)子系统(WSL) (1) 在WSL的Ubuntu20.04下安装必要的工具的. 安装git: sudo apt-get install git …

基于SpringBoot实现的前后端分离书店项目,功能:注册登录、浏览商品、热门商品、购物车、购买、地址管理、密码管理等

一、项目简介 本项目主要基于SpringBoot、Mybatis-plus、MySQL、Redis实现的书店管理系统。 本系统是前后端分离的,分别由三个子项目构成:java服务端、用户浏览与购买的前端、管理员管理商品的前端 环境 java 1.8mysql8.0redisvue2.x 管理员子系统功…

05. Springboot admin集成Actuator(一)

目录 1、前言 2、Actuator监控端点 2.1、健康检查 2.2、信息端点 2.3、环境信息 2.4、度量指标 2.5、日志文件查看 2.6、追踪信息 2.7、Beans信息 2.8、Mappings信息 3、快速使用 2.1、添加依赖 2.2、添加配置文件 2.3、启动程序 4、自定义端点Endpoint 5、自定…