SpringBoot的测试

🙈作者简介:练习时长两年半的Java up主
🙉个人主页:程序员老茶
🙊 ps:点赞👍是免费的,却可以让写博客的作者开心好久好久😎
📚系列专栏:Java全栈,计算机系列(火速更新中)
💭 格言:种一棵树最好的时间是十年前,其次是现在
🏡动动小手,点个关注不迷路,感谢宝子们一键三连

目录

  • 课程名:Java
    • 内容/作用:知识点/设计/实验/作业/练习
    • 学习:SpringBoot的测试
    • SpringBoot的测试
      • 1. 加载测试专用属性
      • 2. 加载测试专用配置
      • 3. Web环境模拟测试
      • 4. 数据层测试回滚
      • 5. 测试用例数据设定

课程名:Java

内容/作用:知识点/设计/实验/作业/练习

学习:SpringBoot的测试

SpringBoot的测试

​ 说完bean配置相关的内容,下面要对前面讲过的一个知识做加强了,测试。测试是保障程序正确性的唯一屏障,在企业级开发中更是不可缺少,但是由于测试代码往往不产生实际效益,所以一些小型公司并不是很关注,导致一些开发者从小型公司进入中大型公司后,往往这一块比较短板,所以还是要拿出来把这一块知识好好说说,做一名专业的开发人员。

1. 加载测试专用属性

​ 测试过程本身并不是一个复杂的过程,但是很多情况下测试时需要模拟一些线上情况,或者模拟一些特殊情况。如果当前环境按照线上环境已经设定好了,例如是下面的配置

env:
  maxMemory: 32GB
  minMemory: 16GB

​ 但是你现在想测试对应的兼容性,需要测试如下配置

env:
  maxMemory: 16GB
  minMemory: 8GB

​ 这个时候我们能不能每次测试的时候都去修改源码application.yml中的配置进行测试呢?显然是不行的。每次测试前改过来,每次测试后改回去,这太麻烦了。于是我们就想,需要在测试环境中创建一组临时属性,去覆盖我们源码中设定的属性,这样测试用例就相当于是一个独立的环境,能够独立测试,这样就方便多了。

临时属性

​ springboot已经为我们开发者早就想好了这种问题该如何解决,并且提供了对应的功能入口。在测试用例程序中,可以通过对注解@SpringBootTest添加属性来模拟临时属性,具体如下:

//properties属性可以为当前测试用例添加临时的属性配置
@SpringBootTest(properties = {"test.prop=testValue1"})
public class PropertiesAndArgsTest {

    @Value("${test.prop}")
    private String msg;
    
    @Test
    void testProperties(){
        System.out.println(msg);
    }
}

​ 使用注解@SpringBootTest的properties属性就可以为当前测试用例添加临时的属性,覆盖源码配置文件中对应的属性值进行测试。

临时参数

​ 除了上述这种情况,在前面讲解使用命令行启动springboot程序时讲过,通过命令行参数也可以设置属性值。而且线上启动程序时,通常都会添加一些专用的配置信息。作为运维人员他们才不懂java,更不懂这些配置的信息具体格式该怎么写,那如果我们作为开发者提供了对应的书写内容后,能否提前测试一下这些配置信息是否有效呢?当时是可以的,还是通过注解@SpringBootTest的另一个属性来进行设定。

//args属性可以为当前测试用例添加临时的命令行参数
@SpringBootTest(args={"--test.prop=testValue2"})
public class PropertiesAndArgsTest {
    
    @Value("${test.prop}")
    private String msg;
    
    @Test
    void testProperties(){
        System.out.println(msg);
    }
}

​ 使用注解@SpringBootTest的args属性就可以为当前测试用例模拟命令行参数并进行测试。

​ 说到这里,好奇宝宝们肯定就有新问题了,如果两者共存呢?其实如果思考一下配置属性与命令行参数的加载优先级,这个结果就不言而喻了。在属性加载的优先级设定中,有明确的优先级设定顺序,还记得下面这个顺序吗?

​ 在这个属性加载优先级的顺序中,明确规定了命令行参数的优先级排序是11,而配置属性的优先级是3,结果不言而喻了,args属性配置优先于properties属性配置加载。

​ 到这里我们就掌握了如果在测试用例中去模拟临时属性的设定。

总结

  1. 加载测试临时属性可以通过注解@SpringBootTest的properties和args属性进行设定,此设定应用范围仅适用于当前测试用例

思考

​ 应用于测试环境的临时属性解决了,如果想在测试的时候临时加载一些bean能不做呢?也就是说我测试时,想搞一些独立的bean出来,专门应用于测试环境,能否实现呢?咱们下一节再讲。

2. 加载测试专用配置

​ 上一节提出了临时配置一些专用于测试环境的bean的需求,这一节我们就来解决这个问题。

​ 学习过Spring的知识,我们都知道,其实一个spring环境中可以设置若干个配置文件或配置类,若干个配置信息可以同时生效。现在我们的需求就是在测试环境中再添加一个配置类,然后启动测试环境时,生效此配置就行了。其实做法和spring环境中加载多个配置信息的方式完全一样。具体操作步骤如下:

步骤①:在测试包test中创建专用的测试环境配置类

@Configuration
public class MsgConfig {
    @Bean
    public String msg(){
        return "bean msg";
    }
}

​ 上述配置仅用于演示当前实验效果,实际开发可不能这么注入String类型的数据

步骤②:在启动测试环境时,导入测试环境专用的配置类,使用@Import注解即可实现

@SpringBootTest
@Import({MsgConfig.class})
public class ConfigurationTest {

    @Autowired
    private String msg;

    @Test
    void testConfiguration(){
        System.out.println(msg);
    }
}

​ 到这里就通过@Import属性实现了基于开发环境的配置基础上,对配置进行测试环境的追加操作,实现了1+1的配置环境效果。这样我们就可以实现每一个不同的测试用例加载不同的bean的效果,丰富测试用例的编写,同时不影响开发环境的配置。

总结

  1. 定义测试环境专用的配置类,然后通过@Import注解在具体的测试中导入临时的配置,例如测试用例,方便测试过程,且上述配置不影响其他的测试类环境

思考

​ 当前我们已经可以实现业务层和数据层的测试,并且通过临时配置,控制每个测试用例加载不同的测试数据。但是实际企业开发不仅要保障业务层与数据层的功能安全有效,也要保障表现层的功能正常。但是我们目的对表现层的测试都是通过postman手工测试的,并没有在打包过程中体现表现层功能被测试通过。能否在测试用例中对表现层进行功能测试呢?还真可以,咱们下一节再讲。

3. Web环境模拟测试

​ 在测试中对表现层功能进行测试需要一个基础和一个功能。所谓的一个基础是运行测试程序时,必须启动web环境,不然没法测试web功能。一个功能是必须在测试程序中具备发送web请求的能力,不然无法实现web功能的测试。所以在测试用例中测试表现层接口这项工作就转换成了两件事,一,如何在测试类中启动web测试,二,如何在测试类中发送web请求。下面一件事一件事进行,先说第一个

测试类中启动web环境

​ 每一个springboot的测试类上方都会标准@SpringBootTest注解,而注解带有一个属性,叫做webEnvironment。通过该属性就可以设置在测试用例中启动web环境,具体如下:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class WebTest {	
}

​ 测试类中启动web环境时,可以指定启动的Web环境对应的端口,springboot提供了4种设置值,分别如下:

  • MOCK:根据当前设置确认是否启动web环境,例如使用了Servlet的API就启动web环境,属于适配性的配置
  • DEFINED_PORT:使用自定义的端口作为web服务器端口
  • RANDOM_PORT:使用随机端口作为web服务器端口
  • NONE:不启动web环境

​ 通过上述配置,现在启动测试程序时就可以正常启用web环境了,建议大家测试时使用RANDOM_PORT,避免代码中因为写死设定引发线上功能打包测试时由于端口冲突导致意外现象的出现。就是说你程序中写了用8080端口,结果线上环境8080端口被占用了,结果你代码中所有写的东西都要改,这就是写死代码的代价。现在你用随机端口就可以测试出来你有没有这种问题的隐患了。

​ 测试环境中的web环境已经搭建好了,下面就可以来解决第二个问题了,如何在程序代码中发送web请求。

测试类中发送请求

​ 对于测试类中发送请求,其实java的API就提供对应的功能,只不过平时各位小伙伴接触的比较少,所以较为陌生。springboot为了便于开发者进行对应的功能开发,对其又进行了包装,简化了开发步骤,具体操作如下:

步骤①:在测试类中开启web虚拟调用功能,通过注解@AutoConfigureMockMvc实现此功能的开启

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
@AutoConfigureMockMvc
public class WebTest {
}

步骤②:定义发起虚拟调用的对象MockMVC,通过自动装配的形式初始化对象

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
@AutoConfigureMockMvc
public class WebTest {

    @Test
    void testWeb(@Autowired MockMvc mvc) {
    }
}

步骤③:创建一个虚拟请求对象,封装请求的路径,并使用MockMVC对象发送对应请求

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
//开启虚拟MVC调用
@AutoConfigureMockMvc
public class WebTest {

    @Test
    void testWeb(@Autowired MockMvc mvc) throws Exception {
        //http://localhost:8080/books
        //创建虚拟请求,当前访问/books
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        //执行对应的请求
        mvc.perform(builder);
    }
}

​ 执行测试程序,现在就可以正常的发送/books对应的请求了,注意访问路径不要写http://localhost:8080/books,因为前面的服务器IP地址和端口使用的是当前虚拟的web环境,无需指定,仅指定请求的具体路径即可。

总结

  1. 在测试类中测试web层接口要保障测试类启动时启动web容器,使用@SpringBootTest注解的webEnvironment属性可以虚拟web环境用于测试
  2. 为测试方法注入MockMvc对象,通过MockMvc对象可以发送虚拟请求,模拟web请求调用过程

思考

​ 目前已经成功的发送了请求,但是还没有起到测试的效果,测试过程必须出现预计值与真实值的比对结果才能确认测试结果是否通过,虚拟请求中能对哪些请求结果进行比对呢?咱们下一节再讲。

web环境请求结果比对

​ 上一节已经在测试用例中成功的模拟出了web环境,并成功的发送了web请求,本节就来解决发送请求后如何比对发送结果的问题。其实发完请求得到的信息只有一种,就是响应对象。至于响应对象中包含什么,就可以比对什么。常见的比对内容如下:

  • 响应状态匹配

    @Test
    void testStatus(@Autowired MockMvc mvc) throws Exception {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        ResultActions action = mvc.perform(builder);
        //设定预期值 与真实值进行比较,成功测试通过,失败测试失败
        //定义本次调用的预期值
        StatusResultMatchers status = MockMvcResultMatchers.status();
        //预计本次调用时成功的:状态200
        ResultMatcher ok = status.isOk();
        //添加预计值到本次调用过程中进行匹配
        action.andExpect(ok);
    }
    
  • 响应体匹配(非json数据格式)

    @Test
    void testBody(@Autowired MockMvc mvc) throws Exception {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        ResultActions action = mvc.perform(builder);
        //设定预期值 与真实值进行比较,成功测试通过,失败测试失败
        //定义本次调用的预期值
        ContentResultMatchers content = MockMvcResultMatchers.content();
        ResultMatcher result = content.string("springboot2");
        //添加预计值到本次调用过程中进行匹配
        action.andExpect(result);
    }
    
  • 响应体匹配(json数据格式,开发中的主流使用方式)

    @Test
    void testJson(@Autowired MockMvc mvc) throws Exception {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        ResultActions action = mvc.perform(builder);
        //设定预期值 与真实值进行比较,成功测试通过,失败测试失败
        //定义本次调用的预期值
        ContentResultMatchers content = MockMvcResultMatchers.content();
        ResultMatcher result = content.json("{\"id\":1,\"name\":\"springboot2\",\"type\":\"springboot\"}");
        //添加预计值到本次调用过程中进行匹配
        action.andExpect(result);
    }
    
  • 响应头信息匹配

    @Test
    void testContentType(@Autowired MockMvc mvc) throws Exception {
        MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
        ResultActions action = mvc.perform(builder);
        //设定预期值 与真实值进行比较,成功测试通过,失败测试失败
        //定义本次调用的预期值
        HeaderResultMatchers header = MockMvcResultMatchers.header();
        ResultMatcher contentType = header.string("Content-Type", "application/json");
        //添加预计值到本次调用过程中进行匹配
        action.andExpect(contentType);
    }
    

​ 基本上齐了,头信息,正文信息,状态信息都有了,就可以组合出一个完美的响应结果比对结果了。以下范例就是三种信息同时进行匹配校验,也是一个完整的信息匹配过程。

@Test
void testGetById(@Autowired MockMvc mvc) throws Exception {
    MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/books");
    ResultActions action = mvc.perform(builder);

    StatusResultMatchers status = MockMvcResultMatchers.status();
    ResultMatcher ok = status.isOk();
    action.andExpect(ok);

    HeaderResultMatchers header = MockMvcResultMatchers.header();
    ResultMatcher contentType = header.string("Content-Type", "application/json");
    action.andExpect(contentType);

    ContentResultMatchers content = MockMvcResultMatchers.content();
    ResultMatcher result = content.json("{\"id\":1,\"name\":\"springboot\",\"type\":\"springboot\"}");
    action.andExpect(result);
}

总结

  1. web虚拟调用可以对本地虚拟请求的返回响应信息进行比对,分为响应头信息比对、响应体信息比对、响应状态信息比对

4. 数据层测试回滚

​ 当前我们的测试程序可以完美的进行表现层、业务层、数据层接口对应的功能测试了,但是测试用例开发完成后,在打包的阶段由于test生命周期属于必须被运行的生命周期,如果跳过会给系统带来极高的安全隐患,所以测试用例必须执行。但是新的问题就呈现了,测试用例如果测试时产生了事务提交就会在测试过程中对数据库数据产生影响,进而产生垃圾数据。这个过程不是我们希望发生的,作为开发者测试用例该运行运行,但是过程中产生的数据不要在我的系统中留痕,这样该如何处理呢?

​ springboot早就为开发者想到了这个问题,并且针对此问题给出了最简解决方案,在原始测试用例中添加注解@Transactional即可实现当前测试用例的事务不提交。当程序运行后,只要注解@Transactional出现的位置存在注解@SpringBootTest,springboot就会认为这是一个测试程序,无需提交事务,所以也就可以避免事务的提交。

@SpringBootTest
@Transactional
@Rollback(true)
public class DaoTest {
    @Autowired
    private BookService bookService;

    @Test
    void testSave(){
        Book book = new Book();
        book.setName("springboot3");
        book.setType("springboot3");
        book.setDescription("springboot3");

        bookService.save(book);
    }
}

​ 如果开发者想提交事务,也可以,再添加一个@RollBack的注解,设置回滚状态为false即可正常提交事务,是不是很方便?springboot在辅助开发者日常工作这一块展现出了惊人的能力,实在太贴心了。

总结

  1. 在springboot的测试类中通过添加注解@Transactional来阻止测试用例提交事务
  2. 通过注解@Rollback控制springboot测试类执行结果是否提交事务,需要配合注解@Transactional使用

思考

​ 当前测试程序已经近乎完美了,但是由于测试用例中书写的测试数据属于固定数据,往往失去了测试的意义,开发者可以针对测试用例进行针对性开发,这样就有可能出现测试用例不能完美呈现业务逻辑代码是否真实有效的达成业务目标的现象,解决方案其实很容易想,测试用例的数据只要随机产生就可以了,能实现吗?咱们下一节再讲。

5. 测试用例数据设定

​ 对于测试用例的数据固定书写肯定是不合理的,springboot提供了在配置中使用随机值的机制,确保每次运行程序加载的数据都是随机的。具体如下:

testcase:
  book:
    id: ${random.int}
    id2: ${random.int(10)}
    type: ${random.int!5,10!}
    name: ${random.value}
    uuid: ${random.uuid}
    publishTime: ${random.long}

​ 当前配置就可以在每次运行程序时创建一组随机数据,避免每次运行时数据都是固定值的尴尬现象发生,有助于测试功能的进行。数据的加载按照之前加载数据的形式,使用@ConfigurationProperties注解即可

@Component
@Data
@ConfigurationProperties(prefix = "testcase.book")
public class BookCase {
    private int id;
    private int id2;
    private int type;
    private String name;
    private String uuid;
    private long publishTime;
}

​ 对于随机值的产生,还有一些小的限定规则,比如产生的数值性数据可以设置范围等,具体如下:

  • ${random.int}表示随机整数
  • ${random.int(10)}表示10以内的随机数
  • ${random.int(10,20)}表示10到20的随机数
  • 其中()可以是任意字符,例如[],!!均可

总结

  1. 使用随机数据可以替换测试用例中书写的固定数据,提高测试用例中的测试数据有效性
往期专栏
Java全栈开发
数据结构与算法
计算机组成原理
操作系统
数据库系统
物联网控制原理与技术

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

CentOS7部署bitbucket7.21.20-postgresql版

文章目录 1.资源下载2.上传服务器3.赋予执行权限并执行安装4.执行安装5.访问Bitbucket6.Bitbucket安装页面配置6.1连接Jira应用程序到PostgreSQL6.1.1创建并配置PostgreSQL数据库6.1.2测试数据库连接 6.2授权6.2.1授权包6.2.1.1资源地址6.2.1.2上传到服务器 6.2.2停止bitbucket…

PDF控件Spire.PDF for .NET【安全】演示:在 PDF 中添加或删除数字签名

随着 PDF 文档在商业中越来越流行,确保其真实性已成为一个关键问题。使用基于证书的签名对 PDF 进行签名可以保护内容,还可以让其他人知道谁签署或批准了该文档。在本文中,您将了解如何使用不可见或可见签名对 PDF 进行数字签名,以…

用CSDN训练的InsCode AI创作博文:数据治理体系建设

想不想用AI帮我们写方案? 想尝试用CSDN提供的InsCode AI创作助手协助我们进行技术方案的创作,看看效果如何,能不能辅助我们日常的方案编写与创作?以前用ChatGPT也尝试过,但对于专业性更强的内容,还有表现的…

VMware虚拟机安装Ubuntu系统教程

所使用的文件如下: VMware Workstation 17 Pro ubuntu-22.04.3-desktop-amd64.iso 一、ubuntu 命名规则及各版本一览表 1.ubuntu 命名规则: 例如:ubuntu 16.04 LTS 是长期维护版本;ubuntu 17.04 是新特性版本 前两位数字为发…

【Unity学习笔记】1.创建场景

创建场景 注1:samplescene(示例场景)、standard assets(标准资产)、favorites(收藏夹)、terrain(地形)。 注2:favorites用于存放各种资源;sample…

【小白专用】Apache下禁止显示网站目录结构的方法 更新23.12.25

给我一个网站地址,我点开后显示的是目录格式,把网站的目录结构全部显示出来了 这个显示结果不正确,不应该让用户看到我们的目录结构 配置文件的问题,apache配置文件里有一项可以禁止显示网站目录的配置项,禁止掉就好了 在apache…

基于ssm出租车管理系统的设计与实现论文

摘 要 现代经济快节奏发展以及不断完善升级的信息化技术,让传统数据信息的管理升级为软件存储,归纳,集中处理数据信息的管理方式。本出租车管理系统就是在这样的大环境下诞生,其可以帮助管理者在短时间内处理完毕庞大的数据信息&…

做外贸其实也并没有那么容易

最近无意中看到一个视频,视频的内容是外贸博主和一个连线人的对话,连线人的诉求是如何做外贸?怎么能把外贸做好, 通过博主和这个人的沟通来看,这个连线的人他从来没有接触过外贸,也不愿意去别的外贸公司上…

图像ISP处理——畸变校正算法

图像畸变校正算法主要用于矫正图像中因为摄像机镜头畸变而引起的形状和尺寸变化。摄像机镜头畸变主要包括径向畸变和切向畸变。以下是一些常见的图像畸变校正算法: 多项式畸变校正法(Polynomial Distortion Correction): 原理&am…

Java:LocalDate / LocalDateTime加减时间

在线API参考:LocalTime (Java Platform SE 8 ) 方法介绍 方法1方法1说明plusYears(long years) minusYears(long years) 返回增加/减少了年数的副本plusMonths(long months) minusMonths(long months)返回增加/减少了月数的副本plusWeeks(long weeks) minusWeeks(…

Wafer晶圆封装工艺介绍

芯片封装的目的(The purpose of chip packaging): 芯片上的IC管芯被切割以进行管芯间连接,通过引线键合连接外部引脚,然后进行成型,以保护电子封装器件免受环境污染(水分、温度、污染物等)&…

Linux开发工具——gcc篇

gcc的使用 文章目录 gcc的使用 历史遗留问题(普通用户sudo) gcc编译过程 预处理(进行宏替换) 编译(生成汇编) 汇编(生成机器可识别代码) 链接(生成可执行文件或库文件&a…

【下载前必读】CSDN发布的付费资源合集(wx有优惠)

【下载前必读】CSDN发布的付费资源合集(wx有优惠) wx csdn付费下载(wx八折起) 可点击标题超链接查看详情 hnust javaweb课设&数据库课设 报告源代码流程图文件课设指导书附赠数据库课堂实验指导书.zip 售价 14.9r&#xff…

安卓拍照扫描APP NDK开发——基于深度学习目标分割实现拍照文档边缘检测

前言 这是一个安卓NDK的项目,想要实现的效果就是拍照扫描,这里只涉及到的只有边缘检测,之后会写文档滤镜、证件识别与证件1比1打印,OCR、版面分析之后的文档还原。我的开发环境是Android Studio 北极狐,真机是华为mat…

apache禁止遍历目录

禁止Apache显示目录索引,禁止Apache显示目录结构列表,禁止Apache浏览目录,这是网上提问比较多的,其实都是一个意思。下面说下禁止禁止Apache显示目录索引的常见的3种方法。 要实现禁止Apache显示目录索引,只需将 Optio…

电子科大软件系统架构设计——设计模式

设计模式概述 设计模式的背景 设计面向对象软件比较困难,而设计可以复用的面向对象软件更加困难不是解决任何问题都需要从头做起,最好能复用以往的设计方案经验面向对象软件设计经验需要有一定的模式记录下来,以提供给其他设计者使用&#…

程序员八大硬技能,熟练掌握能轻松超过90%同行!

每一位程序员都希望能在编程世界中实现自己的梦想,程序员想要不断向上攀爬,需要不断学习新知识,掌握硬技能和软技能,才能超越同龄人,跟上时代的步伐。 今天给大家分享程序员八大硬技能和六大软技能,希望能…

运维工程师的出路到底在哪里?

/bin/bash 运维工程师的出路到底在哪里? 你是不是也常常听到身边的运维人员抱怨,他们的出路到底在哪里呢?别着急,让我告诉你,运维人员就像是IT界的“万金油”,他们像“修理工”一样维修服务器,…

GoogleNet网络分析与demo实例

参考自 up主的b站链接:霹雳吧啦Wz的个人空间-霹雳吧啦Wz个人主页-哔哩哔哩视频这位大佬的博客 Fun_机器学习,pytorch图像分类,工具箱-CSDN博客 1. GoogLeNet网络详解 GoogLeNet在2014年由Google团队提出(与VGG网络同年,注意GoogLeNet中的L大…

概率论1:下象棋问题(3.5)

每日小语 时刻望着他人的眼色行事,是腾飞不了的。自己怎么想就积极地去做,这是需要胆量的。——广中平佑 题目 甲、乙二人下象棋, 每局甲胜的概率为a,乙胜的概率为b. 为简化问题,设没有和局的情况,这意味着a b1. 设想…