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

您可以编写不同类型的集成测试。 从持久性测试开始,您可以检查组件之间的交互,也可以模拟调用外部服务。 本文将讨论后一种情况。 在谈论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

使用WireMock进行更好的集成测试相关推荐

  1. wiremock 使用_使用WireMock进行更好的集成测试

    wiremock 使用 无论您是遵循传统的测试金字塔还是采用诸如" 测试蜂窝"这样的较新方法,都应该在开发过程中的某个时候开始编写集成测试. 您可以编写多种类型的集成测试. 从持久 ...

  2. 集成学习_使用WireMock进行更好的集成测试

    集成学习 无论您是遵循传统的测试金字塔还是采用诸如"测试蜂窝"这样的较新方法,都应该在开发过程中的某个时候开始编写集成测试.您可以编写不同类型的集成测试. 从持久性测试开始,您可以 ...

  3. Fluent Web API集成测试

    目录 介绍 概念 设置 网络API服务 演示集成测试DLL Clifton.IntegrationTestWorkflowEngine DLL 工作流测试方法DLL 我们的第一个Fluent集成测试 ...

  4. 可以进行单元测试么_大量实例助攻,让你的单元测试更高效

    关注[搜狐技术产品]公众号,第一时间获取技术干货 导读 单元测试作为程序员的必修课,对代码的稳定性起着关键性的作用,但是你真的会写单元测试么?什么才算是真正的单元测试?这些疑问你都将在文章中得到解答. ...

  5. WireMock 模拟接口

    上一节课中给大家介绍了如何搭建 WireMock 服务并模拟了一个简单的 Get 请求接口.为了后续在学习接口测试过程中能模拟各种类型的接口,本次课程将带领大家学习如何通过 WireMock 模拟更复 ...

  6. 如何编写可测试的golang代码

    每次在开发之前,我都会考虑写好单元测试,但是随着开发的进行,就会发现事情没有这么简单,因为更多时候项目中间夹杂着很多的数据库操作,网络操作,文件操作等等,每次涉及到有这些操作的单元测试,都要花费很大的 ...

  7. 今天的面试官是个锤子,Spring为什么建议使用构造器来注入?

    欢迎关注方志朋的博客,回复"666"获面试宝典 本文的内容主要是想探讨我们在进行Spring 开发过程当中,关于依赖注入的几个知识点.感兴趣的读者可以先看下以下问题: @Autow ...

  8. Spring官方为什么建议构造器注入?

    以下文章来源方志朋的博客,回复"666"获面试宝典 前言 本章的内容主要是想探讨我们在进行 Spring 开发过程当中,关于依赖注入的几个知识点.感兴趣的读者可以先看下以下问题: ...

  9. Spring为什么建议构造器注入?

    以下文章来源方志朋的博客,回复"666"获面试宝典 来源:https://juejin.cn/post/6844904056230690824 前言 本章的内容主要是想探讨我们在进 ...

最新文章

  1. 单纯形法(三)(概念部分)
  2. SegmentFault 专访 | AlloyTeam 2015 前端技术大会讲师圆桌
  3. 发布一个自己写的.Net代码生成器
  4. 算法相关(2)-单向链表
  5. 【数据结构与算法】之深入解析“排列硬币”的求解思路与算法示例
  6. 银华基金:用小型机的群狼战术保驾护航!
  7. [置顶] 我的iOS作品
  8. python中classmethod与staticmethod的差异及应用
  9. 文件分片_怎样屏蔽QQ和微信外发文件,同时允许发送截图
  10. JIRA和Confluence更改JVM内存大小解决访问打开缓慢问题
  11. layui数据表格合并列
  12. 逻辑归纳与数学归纳:皮亚诺公理5解读1——皮亚诺读后之七
  13. 半导体产业链全梳理 哪个细分领域更具投资价值?
  14. 什么是ZOOM?ZOOM的作用是什么?
  15. java中关键字_java中的关键字是什么?
  16. 数据结构的顺序表操作集
  17. TensorFlow on Android:训练模型
  18. 易信电脑版 v2.1.0 PC版
  19. 共建网络安全 共享网络文明 2015福建网络安全宣传日成功举办
  20. MIT-BIH心电数据库的使用

热门文章

  1. nssl1186-字串数量【前缀和】
  2. ssl1759-求连通分量【图论,深搜,广搜】
  3. 森近林之助【字符串处理】
  4. switch case 支持的 6 种数据类型
  5. Spring Boot 发布 jar 包转为 war 包秘籍。
  6. Zookeeper面试题锦集
  7. 若依部署上线之后验证码不显示的解决方法之一
  8. 新闻发布项目——接口类(commentDao)
  9. 2020蓝桥杯省赛---java---B---4( 合并检测)
  10. php无get报错,php $_get报错怎么办