SpringBoot测试及web环境模拟测试

2023-11-30 03:40

本文主要是介绍SpringBoot测试及web环境模拟测试,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

  • 一、加载测试专用属性
    • 1. 临时属性
    • 2. 临时参数
  • 二、加载测试专用配置
  • 三、Web环境模拟测试
    • 1. 启动web测试
    • 2. 测试类中发送请求
    • 3. 响应状态匹配
    • 4. 响应体匹配
    • 5. 响应头匹配
    • 6. 完整信息匹配
  • 四、数据层测试数据回滚
  • 五、测试用例数据设定

一、加载测试专用属性

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

1. 临时属性

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

my:prop: hello-1

未使用临时属性的测试代码

@SpringBootTest
class SpringBootTest2 {@Value("${my.prop}")String msg;@Testvoid contextLoads() {System.out.println(msg);}
}

在这里插入图片描述

使用临时属性的测试代码

@SpringBootTest(properties = {"my.prop=hello-2"})
class SpringBootTest2 {@Value("${my.prop}")String msg;@Testvoid contextLoads() {System.out.println(msg);}
}

通过临时属性的设置就能覆盖我们在yml中的属性设置了

在这里插入图片描述

2. 临时参数

@SpringBootTest(args = {"--my.prop=hello-3"})
class SpringBootTest2 {@Value("${my.prop}")String msg;@Testvoid contextLoads() {System.out.println(msg);}
}

临时参数就是使用 args 代替 临时属性 properties,同时他在每一个参数前都加上了--

如果yml、临时属性、临时参数三种配置情况都存在的话SpringBoot会选择哪一种呢,我们来尝试一下。

@SpringBootTest(args = {"--my.prop=hello-3"}, properties = {"my.prop=hello-2"})
class SpringBootTest2 {@Value("${my.prop}")String msg;@Testvoid contextLoads() {System.out.println(msg);}
}

在这里插入图片描述

答案就是依据临时参数


二、加载测试专用配置

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

  • 在测试包test中创建专用的测试环境配置类
    在这里插入图片描述
@Configuration
public class DBConfig {@Beanpublic String msg(){return "hello-db";}
}
  • 在启动测试环境时,导入测试环境专用的配置类,使用@Import注解即可实现
@SpringBootTest
@Import(DBConfig.class)
class SpringBootTest2 {@Autowiredprivate String msg;@Testvoid contextLoads() {System.out.println(msg);}
}

在这里插入图片描述

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


三、Web环境模拟测试

上述我们都是在数据层与业务层进行测试,那我们可以在表现层上进行测试吗?答案当然是可以。

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

  • 如何在测试类中启动web测试
  • 如何在测试类中发送web请求

1. 启动web测试

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

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

webEnvironment属性值解释

  • SpringBootTest.WebEnvironment.MOCK默认值,该类型提供一个mock环境,可以和@AutoConfigureMockMvc@AutoConfigureWebTestClient搭配使用,开启Mock相关的功能。注意此时内嵌的服务(servlet容器)并没有真正启动,也不会监听web服务端口。
  • SpringBootTest.WebEnvironment.NONE:启动一个非web的ApplicationContext,即不提供mock环境,也不提供真实的web服务。
  • SpringBootTest.WebEnvironment.DEFINED_PORT:按照配置的端口启动web环境。
  • SpringBootTest.WebEnvironment.RANDOM_PORT:随机端口启动web环境。

2. 测试类中发送请求

  • 定义好一个 controller
@RestController
@RequestMapping("/books")
public class BookController {@GetMappingpublic String getBook(){System.out.println("getBook" );return "hello book";}
}
  • 模拟web请求调用过程
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
// 开启虚拟 MVC 调用
@AutoConfigureMockMvc
class SpringBootWebTest {// 定义发起虚拟调用的对象MockMVC,通过自动装配的形式初始化对象@Autowiredprivate MockMvc mockMvc;@Testvoid testWeb() throws Exception {// 创建一个虚拟请求对象,封装请求的路径,并使用MockMVC对象发送对应请求RequestBuilder builder = MockMvcRequestBuilders.get("/book");mockMvc.perform(builder);}
}

可以看出,已经成功调用了 BookController。

在这里插入图片描述

3. 响应状态匹配

在上述我们已经成功发出了请求,但是我们却无法得知表现层的功能是否正常,这就需要我们来进行一个判断。

我们可以把这个过程理解为断言,当程序结果与我们预期相符时,测试通过;当不符时,抛出异常。

在这里,我们可以通过判断响应状态,先来简单判断下 status。

@Test
void testWeb() throws Exception {// 创建一个虚拟请求对象,封装请求的路径,并使用MockMVC对象发送对应请求RequestBuilder builder = MockMvcRequestBuilders.get("/book");ResultActions action = mockMvc.perform(builder);// 设置预期值,与真实值比较. 成功测试通过; 失败测试不通过// 定义本次调用成功的状态 200StatusResultMatchers status = MockMvcResultMatchers.status();// 预计本次调用时成功的 状态 200ResultMatcher ok = status.isOk();// 添加预计本次调用过程中进行匹配action.andExpect(ok);
}

如此后,控制台输出的仍然和上面一样,那怎么去看状态呢。

在这里插入图片描述

我们先来人为制造一个错误,设立一个不存在的路径。

// 创建一个虚拟请求对象,发送到一个不存在的路径,制造错误
RequestBuilder builder = MockMvcRequestBuilders.get("/book11111");

如下图,当请求发生错误后(即真实结果与我们的预期值不匹配),控制台输出了错误原因。也得到了很多本次请求响应的结果。

在这里插入图片描述

通过以上我们也能得出一个结论,当真实值与我们的预期结果不匹配时,才会弹出错误。

4. 响应体匹配

  • 定义一个 controller
@RestController
@RequestMapping("/book")
public class BookController {@GetMapping("/one")public Book getBook2(){System.out.println("book get one");Book book = new Book();book.setId(1);book.setName("西游记");book.setPrice(20.0);return book;}
}
  • 发送请求测试
@Test
void testBody(@Autowired MockMvc mockMvc) throws Exception {RequestBuilder builder = MockMvcRequestBuilders.get("/book/one");ResultActions action = mockMvc.perform(builder);// 设置预期值,与真实值比较,成功测试通过,失测试失败// 定义本次调用成功的状态 200ContentResultMatchers content = MockMvcResultMatchers.content();// 预计本次调用时成功的 状态 200ResultMatcher resultMatcher = content.json("{\"name\":\"西游记\",\"id\":1,\"price\":20.0}");// 添加预计本次调用过程中进行匹配action.andExpect(resultMatcher);
}

5. 响应头匹配

 @Test
public void testHeader(@Autowired MockMvc mvc) throws Exception {
// 创建虚拟请求,当前访问/books
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/book");
// 执行请求
ResultActions action = mvc.perform(builder);
// 匹配执行状态(是否预期值)
// 定义执行状态匹配器
HeaderResultMatchers header = MockMvcResultMatchers.header();
ResultMatcher string = header.string("Content-Type", "application/json");
// 使用本次真实值与预期结果对比
action.andExpect(string);
}

6. 完整信息匹配

上述提到的各种响应匹配都可以放在一起来为请求做断言,只要有一个不匹配,程序都会抛出异常。

@Test
public void testGetById(@Autowired MockMvc mvc) throws Exception {MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/book");ResultActions action = mvc.perform(builder);StatusResultMatchers status = MockMvcResultMatchers.status();ResultMatcher ok = status.isOk();action.andExpect(ok);HeaderResultMatchers header = MockMvcResultMatchers.header();ResultMatcher string = header.string("Content-Type", "application/json");action.andExpect(string);ContentResultMatchers content = MockMvcResultMatchers.content();ResultMatcher result = content.json("{\"name\":\"西游记\",\"id\":1,\"price\":20.0}");action.andExpect(result);
}

四、数据层测试数据回滚

测试用例如果测试时产生了事务提交就会在测试过程中对数据库数据产生影响,进而产生垃圾数据。这个过程不是我们希望发生的,作为开发者测试用例该运行运行,但是过程中产生的数据不要在我的系统中留痕,这样该如何处理呢?

SpringBoot早就为开发者想到了这个问题,并且针对此问题给出了最简解决方案,在原始测试用例中添加注解@Transactional即可实现当前测试用例的事务不提交。

程序运行后,若注解@SpringBootTest出现的位置存在注解@Transactional,SpringBoot就会认为这是一个测试程序,无需提交事务,所以也就可以避免事务的提交。

@SpringBootTest
@Transactional
@Rollback(true)
public class DaoTest {@Autowiredprivate BookService bookService;@Testvoid testSave(){Book book = new Book();book.setName("红楼梦");book.setId(2);book.setPrice(20.0);bookService.save(book);}
}
  • 在SpringBoot的测试类中通过添加注解@Transactional来阻止测试用例提交事务
  • 通过注解@Rollback控制SpringBoot测试类执行结果是否提交事务,当为 true 时,回滚事务;false,不回滚事务。需要配合注解@Transactional使用

五、测试用例数据设定

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

testCase:book:id: ${random.int}name: ${random.value}price: ${random.int}
@Component
@Data
@ConfigurationProperties(prefix = "testcase.book")
public class BookCase {private int id;private String name;private Double price;
}
@SpringBootTest
@Transactional
@Rollback(true)
public class DaoTest {@Autowiredprivate BookService bookService;@Autowiredprivate BookCase bookCase;@Testvoid testSave(){Book book = new Book();book.setName(bookCase.getName());book.setId(bookCase.getId());book.setPrice(bookCase.getPrice());bookService.save(book);}
}
${random.int}              # 随机整数
${random.int(10)}          # 10以内随机数
${random.int(10, 20)}    # 10 到 20 随机数
${random.uuid}             # 随机 uuid
${random.value}            # 随机字符串,MD5字符串,32位   
${random.long}             # 随机整数(long 范围)

这篇关于SpringBoot测试及web环境模拟测试的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/435489

相关文章

SpringBoot全局域名替换的实现

《SpringBoot全局域名替换的实现》本文主要介绍了SpringBoot全局域名替换的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一... 目录 项目结构⚙️ 配置文件application.yml️ 配置类AppProperties.Ja

Java使用Javassist动态生成HelloWorld类

《Java使用Javassist动态生成HelloWorld类》Javassist是一个非常强大的字节码操作和定义库,它允许开发者在运行时创建新的类或者修改现有的类,本文将简单介绍如何使用Javass... 目录1. Javassist简介2. 环境准备3. 动态生成HelloWorld类3.1 创建CtC

JavaScript中的高级调试方法全攻略指南

《JavaScript中的高级调试方法全攻略指南》什么是高级JavaScript调试技巧,它比console.log有何优势,如何使用断点调试定位问题,通过本文,我们将深入解答这些问题,带您从理论到实... 目录观点与案例结合观点1观点2观点3观点4观点5高级调试技巧详解实战案例断点调试:定位变量错误性能分

Java实现将HTML文件与字符串转换为图片

《Java实现将HTML文件与字符串转换为图片》在Java开发中,我们经常会遇到将HTML内容转换为图片的需求,本文小编就来和大家详细讲讲如何使用FreeSpire.DocforJava库来实现这一功... 目录前言核心实现:html 转图片完整代码场景 1:转换本地 HTML 文件为图片场景 2:转换 H

Java使用jar命令配置服务器端口的完整指南

《Java使用jar命令配置服务器端口的完整指南》本文将详细介绍如何使用java-jar命令启动应用,并重点讲解如何配置服务器端口,同时提供一个实用的Web工具来简化这一过程,希望对大家有所帮助... 目录1. Java Jar文件简介1.1 什么是Jar文件1.2 创建可执行Jar文件2. 使用java

SpringBoot实现不同接口指定上传文件大小的具体步骤

《SpringBoot实现不同接口指定上传文件大小的具体步骤》:本文主要介绍在SpringBoot中通过自定义注解、AOP拦截和配置文件实现不同接口上传文件大小限制的方法,强调需设置全局阈值远大于... 目录一  springboot实现不同接口指定文件大小1.1 思路说明1.2 工程启动说明二 具体实施2

Java实现在Word文档中添加文本水印和图片水印的操作指南

《Java实现在Word文档中添加文本水印和图片水印的操作指南》在当今数字时代,文档的自动化处理与安全防护变得尤为重要,无论是为了保护版权、推广品牌,还是为了在文档中加入特定的标识,为Word文档添加... 目录引言Spire.Doc for Java:高效Word文档处理的利器代码实战:使用Java为Wo

SpringBoot日志级别与日志分组详解

《SpringBoot日志级别与日志分组详解》文章介绍了日志级别(ALL至OFF)及其作用,说明SpringBoot默认日志级别为INFO,可通过application.properties调整全局或... 目录日志级别1、级别内容2、调整日志级别调整默认日志级别调整指定类的日志级别项目开发过程中,利用日志

Java中的抽象类与abstract 关键字使用详解

《Java中的抽象类与abstract关键字使用详解》:本文主要介绍Java中的抽象类与abstract关键字使用详解,本文通过实例代码给大家介绍的非常详细,感兴趣的朋友跟随小编一起看看吧... 目录一、抽象类的概念二、使用 abstract2.1 修饰类 => 抽象类2.2 修饰方法 => 抽象方法,没有

SpringBoot 多环境开发实战(从配置、管理与控制)

《SpringBoot多环境开发实战(从配置、管理与控制)》本文详解SpringBoot多环境配置,涵盖单文件YAML、多文件模式、MavenProfile分组及激活策略,通过优先级控制灵活切换环境... 目录一、多环境开发基础(单文件 YAML 版)(一)配置原理与优势(二)实操示例二、多环境开发多文件版