使用WireMock进行更好的集成测试

无论您是遵循传统的测试金字塔还是采用诸如“ 测试蜂窝”这样的较新方法,都应该在开发过程中的某个时候开始编写集成测试。

您可以编写不同类型的集成测试。 从持久性测试开始,您可以检查组件之间的交互,也可以模拟调用外部服务。 本文将讨论后一种情况。 在谈论WireMock之前,让我们从一个激励性的例子开始。

ChuckNorrisFact服务

完整的示例可以在GitHub上找到 。

在以前的博客文章中,您可能已经看到我使用Chuck Norris事实API 。 该API将为我们提供实现所依赖的另一项服务的示例。 我们有一个简单的ChuckNorrisFactController作为用于手动测试的API。 “业务”类旁边是ChuckNorrisService ,用于调用外部API。 它使用Spring的RestTemplate 。 没什么特别的。 我多次看到的是模拟RestTemplate并返回一些预先确定的答案的测试。 该实现可能如下所示:

 @Service  public class ChuckNorrisService{  ... public ChuckNorrisFact retrieveFact() { ResponseEntity<ChuckNorrisFactResponse> response = restTemplate.getForEntity(url, ChuckNorrisFactResponse. class ); return Optional.ofNullable(response.getBody()).map(ChuckNorrisFactResponse::getFact).orElse(BACKUP_FACT); } ... } 

在检查成功案例的常规单元测试旁边,将至少有一项覆盖错误案例的测试,即4xx或5xx状态代码:

 @Test public void shouldReturnBackupFactInCaseOfError() { String url = " http://localhost:8080 " ; RestTemplate mockTemplate = mock(RestTemplate. class ); ResponseEntity<ChuckNorrisFactResponse> responseEntity = new ResponseEntity<>(HttpStatus.SERVICE_UNAVAILABLE); when(mockTemplate.getForEntity(url, ChuckNorrisFactResponse. class )).thenReturn(responseEntity); var service = new ChuckNorrisService(mockTemplate, url); ChuckNorrisFact retrieved = service.retrieveFact(); assertThat(retrieved).isEqualTo(ChuckNorrisService.BACKUP_FACT); } 

看起来还不错吧? 响应实体返回503错误代码,我们的服务不会崩溃。 所有测试都是绿色的,我们可以部署我们的应用程序。

不幸的是,Spring的RestTemplate不能这样工作。 getForEntity的方法签名为我们提供了一个非常小的提示。 它指出throws RestClientException 。 这就是模拟的RestTemplate与实际实现不同的地方。 我们将永远不会收到带有4xx或5xx状态代码的ResponseEntity 。 RestTemplate将抛出一个子类 RestClientException 。 通过查看类的层次结构,我们可以对可能抛出的结果有一个很好的印象:


因此,让我们看看如何使这项测试更好。

WireMock进行救援

WireMock通过启动模拟服务器并返回将其配置为返回的答案来模拟Web服务。 得益于出色的DSL,它很容易集成到您的测试中,并且模拟请求也很简单。

对于JUnit 4,有一个WireMockRule可以帮助启动停止服务器。 对于JUnit 5,您必须自己做。 当您检查示例项目时,您可以找到ChuckNorrisServiceIntegrationTest 。 这是基于JUnit 4的SpringBoot测试。让我们看一下。 最重要的部分是ClassRule

 @ClassRule public static WireMockRule wireMockRule = new WireMockRule(); 

如前所述,这将启动和停止WireMock服务器。 您也可以使用常规Rule来为每个测试启动和停止服务器。 对于我们的测试,这不是必需的。

接下来,您可以看到几种configureWireMockFor...方法。 这些包含WireMock何时返回答案的说明。 将WireMock配置分为几种方法并从测试中调用它们是我使用WireMock的方法。 当然,您可以在@Before方法中设置所有可能的请求。 对于成功案例,我们这样做:

 public void configureWireMockForOkResponse(ChuckNorrisFact fact) JsonProcessingException { configureWireMockForOkResponse(ChuckNorrisFact fact) throws JsonProcessingException { ChuckNorrisFactResponse chuckNorrisFactResponse = new ChuckNorrisFactResponse chuckNorrisFactResponse = ChuckNorrisFactResponse( "success" , fact); stubFor(get(urlEqualTo( "/jokes/random" )) .willReturn(okJson(OBJECT_MAPPER.writeValueAsString(chuckNorrisFactResponse)))); } 

所有方法都是从com.github.tomakehurst.wiremock.client.WireMock静态com.github.tomakehurst.wiremock.client.WireMock 。 如您所见,我们将HTTP GET存入路径/jokes/random并返回一个JSON对象。 的
okJson()方法只是JSON内容的200个响应的简写。 对于错误情况,代码甚至更简单:

 private void configureWireMockForErrorResponse() { stubFor(get(urlEqualTo( "/jokes/random" )) .willReturn(serverError())); } 

如您所见,DSL使阅读说明变得容易。

将WireMock放置在适当的位置,我们可以看到我们先前的实现不起作用,因为RestTemplate引发了异常。 因此,我们必须调整代码:

 public ChuckNorrisFact retrieveFact() { try { ResponseEntity<ChuckNorrisFactResponse> response = restTemplate.getForEntity(url, ChuckNorrisFactResponse. class ); return Optional.ofNullable(response.getBody()).map(ChuckNorrisFactResponse::getFact).orElse(BACKUP_FACT); } catch (HttpStatusCodeException e){ return BACKUP_FACT; } } 

这已经涵盖了WireMock的基本用例。 配置请求的答案,执行测试,检查结果。 就这么简单。

但是,在云环境中运行测试时,通常会遇到一个问题。 让我们看看我们能做什么。

动态端口上的WireMock

您可能已经注意到,项目中的集成测试包含一个
ApplicationContextInitializer类及其@TestPropertySource批注将覆盖实际API的URL。 那是因为我想在随机端口上启动WireMock。 当然,您可以为WireMock配置一个固定端口,并在测试中将此端口用作硬编码值。 但是,如果您的测试在某些云提供商的基础架构上运行,则无法确定该端口是否可用。 因此,我认为随机端口更好。
不过,在Spring应用程序中使用属性时,我们必须以某种方式将随机端口传递给我们的服务。 或者,如您在示例中看到的那样,覆盖URL。 这就是为什么我们使用ApplicationContextInitializer 。 我们将动态分配的端口添加到应用程序上下文中,然后可以使用属性来引用它 ${wiremock.port} 。 这里唯一的缺点是我们现在必须使用ClassRule。 否则,我们无法在初始化Spring应用程序之前访问端口。 解决了此问题后,让我们看一下涉及HTTP调用的一个常见问题。

超时时间

WireMock提供了更多的响应可能性,而不仅仅是对GET请求的简单答复。 经常被遗忘的另一个测试案例是测试超时。 开发人员往往会忘记在RestTemplate甚至URLConnections上设置超时。 如果没有超时,则两者都将等待无限量的时间来进行响应。 在最好的情况下,您不会注意到,在最坏的情况下,所有线程都将等待永远不会到达的响应。

因此,我们应该添加一个模拟超时的测试。 当然,我们也可以使用Mockito模拟来创建延迟,但是在这种情况下,我们将再次猜测RestTemplate的行为。 使用WireMock模拟延迟非常简单:

 private void configureWireMockForSlowResponse() throws JsonProcessingException { ChuckNorrisFactResponse chuckNorrisFactResponse = new ChuckNorrisFactResponse chuckNorrisFactResponse = ChuckNorrisFactResponse( "success" , new ChuckNorrisFact(1L, "" )); stubFor(get(urlEqualTo( "/jokes/random" )) .willReturn( okJson(OBJECT_MAPPER.writeValueAsString(chuckNorrisFactResponse)) .withFixedDelay(( int ) Duration.ofSeconds(10L).toMillis()))); } 

withFixedDelay()需要一个表示毫秒的int值。 我更喜欢使用Duration或至少一个表示该参数表示毫秒的常量,而不必每次都读取JavaDoc。

RestTemplate上设置超时并添加响应缓慢的测试后,我们可以看到RestTemplate抛出ResourceAccessException 。 因此,我们可以调整catch块以捕获此异常和HttpStatusCodeException或者仅捕获两者的超类:

 public ChuckNorrisFact retrieveFact() { try { ResponseEntity<ChuckNorrisFactResponse> response = restTemplate.getForEntity(url, ChuckNorrisFactResponse. class ); return Optional.ofNullable(response.getBody()).map(ChuckNorrisFactResponse::getFact).orElse(BACKUP_FACT); } catch (RestClientException e){ return BACKUP_FACT; } } 

现在,我们已经很好地介绍了执行HTTP请求时最常见的情况,并且可以确定我们正在测试接近真实条件的条件。

为什么不飞翔?

HTTP集成测试的另一个选择是Hoverfly 。 它的工作原理类似于WireMock,但我更喜欢后者。 原因是在运行包含浏览器的端到端测试时,WireMock也非常有用。 Hoverfly(至少是Java库)受JVM代理的限制。 这可能使它比WireMock更快,但是当例如某些JavaScript代码开始起作用时,它根本不起作用。 当您的浏览器代码也直接调用其他一些服务时,WireMock启动Web服务器这一事实非常有用。 然后,您也可以使用WireMock来模拟它们,并编写例如Selenium测试。

结论

我希望本文可以向您展示两件事:

  1. 集成测试的重要性
  2. WireMock非常好

当然,这两个主题都可以填满更多文章。 尽管如此,我还是想让您了解如何使用WireMock及其功能。 随时检查他们的文档,然后尝试更多其他事情。 例如,也可以使用WireMock测试身份验证。

翻译自: https://www.javacodegeeks.com/2019/11/better-integration-tests-with-wiremock.html

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

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

相关文章

Acwing刷题第一节

756 题目分析 &#xff1a; 要点如下&#xff1a; 1. 矩阵遍历&#xff1a;确定遍历方向 2. 确定遍历边界 #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int N 110; int n,m; int res[N][N];int main() {…

Jetty,Java和OAuth入门

使用Okta的身份管理平台轻松部署您的应用程序 使用Okta的API在几分钟之内即可对任何应用程序中的用户进行身份验证&#xff0c;管理和保护。 今天尝试Okta。 Jetty是一个小型&#xff0c;高度可扩展的基于Java的Web服务器和servlet引擎。 它支持HTTP / 2&#xff0c;WebSocket…

vscode 设置 sublime 的主题

vscode 怎么设置 sublime 的主题&#xff1f; VsCode中提供多种主题样式&#xff0c;供使用者选择。 由于习惯于Sublime默认主题&#xff0c;因此将VsCode主题更换为Sublime默认主题。 首先点击编辑器顶部导航菜单中的【文件】&#xff1b; 然后点击【首选项】下【颜色主题】…

VsCode与Sublime编辑器优缺点对比

一 VS code与Sublime共同点 文本功能强大&#xff0c;支持各种语言高亮支持编译&#xff0c;且可以在控制台看到输出可安装大量插件 二 VsCode优点 1. 插件管理成熟 插件很多&#xff0c;近vscode发展势头猛&#xff0c;安装插件方便简单&#xff0c;有插件的详细介绍 vscode新…

spring预加载与懒加载_通过Spring将继承树加载到List中

spring预加载与懒加载我注意到有趣的Spring功能。 我的一位同事使用它将Spring Bean的整个继承树加载到列表中。 在学习Spring文档时错过了这一点。 让我们来看看Spring bean的继承树&#xff1a; 下面的代码片段是通过构造函数注入将此豆树加载到列表中的&#xff1a; Comp…

VsCode连接远程服务器并修改文件代码

一 插件安装配置 1. 安装远程开发插件Remote Development&#xff0c;在商店中搜索Remote Development并安装。 2、选择SSH Targets 安装完成后&#xff0c;vscode左侧多出了一个菜单栏&#xff0c;我们点击配置按钮配置远程服务器的地址。选择SSH Targets。 二 远程服务器配…

Java和Round-Robin上的AtomicInteger

AtomicInteger属于Atomic Variables族。 主要好处是使用它不会阻塞而不是进行阻塞同步&#xff0c;因此避免了线程的挂起和重新调度。 AtomicInteger基于“比较和交换”机制&#xff0c;并且是原子变量的标量组的一部分。 我们的第一个用例是可以多次访问的网页上的功能。 pa…

VsCode打开终端的方法

方法1&#xff1a;打开终端的常规方法 打开VScode后&#xff0c;鼠标左键单击窗口顶部的【查看】&#xff08;如下图红圈标注&#xff09;&#xff0c; 在下拉列表中找到【终端】&#xff08;如下图红框标注&#xff09; 鼠标左键点击【终端】即可打开终端子窗口&#xff0c;如…

Ubuntu下VScode配置ssh免密远程登录

一 实现步骤 1.在本机与远程服务器上&#xff0c; 输入ssh-keygen -t rsa&#xff0c;然后连续回车直到结束 2.在本机上执行命令 ssh-copy-id 命令 &#xff08;1&#xff09;.命令介绍 ssh-copy-id命令可以把本地的ssh公钥文件安装到远程主机对应的账户下。 达到的功能&am…

VSCode隐藏左边活动栏

用sublime时间较长&#xff0c;VsCode左边的活动栏看上去有些多余。查询隐藏活动栏的快捷键&#xff0c;但没有找到。通过vscode怎么隐藏左边栏&#xff1f; - 知乎有快捷键可以隐藏左边栏么&#xff1f;https://www.zhihu.com/question/48285162 问题&#xff0c;找到相关处理…

java 从未导入_Java 8的10个您从未听说过的功能

java 从未导入Lambdas lambdas lambdas。 这是您在谈论Java 8时所听到的所有信息。但这只是一部分。 Java 8具有许多新功能-一些功能强大的新类和习惯用法&#xff0c;而另一些则是从一开始就应该存在的功能。 我想介绍十个新功能&#xff0c;我认为这些功能绝对是值得了解的小…

删除当前路径下含某个关键字的所有文件

一 查找含关键字的所有文件名 此处拿"T5"举例 1. 使用ls命令查找 ls -R | grep T5 # -R 显示文件夹内部所有文件 2. 使用find命令 find . -name "*T5*" 二 将查找到的文件删除 find . -name "*T5*" |xargs rm -rf xargs - build and exec…

Typora设置标题自动标号

Typora由于默认标题无法自动标号&#xff0c;每次编辑时需要手动处理。为实现标题自动编号&#xff0c;需要进行相关操作。 一 官方说明 To achieve this, add the following to your base.user.css or [theme].user.css in the theme folder. /** initialize css counter */ …

捕获Java堆转储的7个选项

堆转储是诊断与内存相关的问题的重要工件&#xff0c;例如内存泄漏缓慢&#xff0c;垃圾回收问题和java.lang.OutOfMemoryError。它们也是优化内存消耗的重要工件。 有很多很棒的工具&#xff0c;例如Eclipse MAT和Heap Hero&#xff0c;可以分析堆转储。 但是&#xff0c;您需…

Ubuntu对CPU进行测试

一 用 stress 工具对CPU进行压力测试 1.软件安装 sudo apt-get update #日常先更新再安装东西不容易出错 #下一條語句可以省略 sudo apt-get install -y linux-tools-$(uname -r) #系统基本功能安装 sudo apt-get install stress #安装stress软件 sudo stress -c 2 -t 100 …

第一章 基础算法(一)

文章目录排序快速排序--分治归并排序二分整数二分浮点数二分整体框架排序 快速排序–分治 785题目&#xff1a; 给定你一个长度为 n 的整数数列。请你使用快速排序对这个数列按照从小到大进行排序。并将排好序的数列按顺序输出。输入格式 输入共两行&#xff0c;第一行包含整数…

java8hashmap_Java 8中的HashMap性能改进

java8hashmapHashMap<K, V>是每个Java程序中快速&#xff0c;通用且无处不在的数据结构。 首先是一些基础知识。 您可能知道&#xff0c;它使用键的hashCode()和equals()方法在存储桶之间拆分值。 存储桶&#xff08;箱&#xff09;的数量应略高于映射中的条目数&#xf…

装前必看施工干货,贴瓷砖的5大步骤。福州中宅装饰,福州装修

亲爱的朋友们&#xff0c;你们是否曾经在装修房屋时遇到过贴砖的难题呢&#xff1f;贴砖可是装修工程中一项重要的工艺&#xff0c;它直接影响到整个装修的效果和质量。今天&#xff0c;我就来跟大家分享一下贴砖的几个重要要点&#xff0c;希望对你们有所帮助。 1️⃣ 选材是关…

Typora+Node.js+PicGo搭建图床

目录 一 问题背景 二 具体步骤 2.1 picgo的安装 1. 下载picgo 2. 安装 3. 效果 2.2 Node.js的安装 (1)下载链接 &#xff08;2&#xff09;安装步骤 2.3 Gitee设置 2.3.1在gitee上面创建一个仓库 2.4 整体配置 2.4.1 picgo软件配置 2.4.2 图床设置 2.4.3 Typora配…