wiremock 使用_使用WireMock进行更好的集成测试
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测试。
结论
我希望本文可以向您展示两件事:
- 集成测试的重要性
- WireMock非常好
当然,这两个主题都可以填满更多文章。 尽管如此,我还是想让您了解如何使用WireMock及其功能。 随时检查他们的文档,然后尝试更多其他事情。 例如,也可以使用WireMock测试身份验证。
翻译自: https://www.javacodegeeks.com/2019/11/better-integration-tests-with-wiremock.html
wiremock 使用
wiremock 使用_使用WireMock进行更好的集成测试相关推荐
- D兔提供伤感日志_不要随便牵手,更不要随便放手
D兔提供伤感日志_不要随便牵手,更不要随便放手 - D兔提供伤感日志_不要随便牵手,更不要随便放手 你发觉了吗? 爱的感觉,总是在一开始觉得很甜蜜, 总觉 - 得多一个人陪.多一个人帮你分担, 你终於 ...
- 集成学习_使用WireMock进行更好的集成测试
集成学习 无论您是遵循传统的测试金字塔还是采用诸如"测试蜂窝"这样的较新方法,都应该在开发过程中的某个时候开始编写集成测试.您可以编写不同类型的集成测试. 从持久性测试开始,您可以 ...
- 使用WireMock进行更好的集成测试
无论您是遵循传统的测试金字塔还是采用诸如" 测试蜂窝"这样的较新方法,都应该在开发过程中的某个时候开始编写集成测试. 您可以编写不同类型的集成测试. 从持久性测试开始,您可以检查组 ...
- 对数据可视化的理解_使数据可视化更容易理解
对数据可视化的理解 Data is weaving its way into almost all aspects of our lives since the past decade. Our ab ...
- 如何更好的掌握一个知识点_如何成为一个更好的讲故事的人3个关键点
如何更好的掌握一个知识点 You're launching a digital transformation initiative in the middle of the ongoing pande ...
- 机器学习 训练较快的模型_通过心理模型更快地学习软件,第1部分
机器学习 训练较快的模型 什么是心理模型? (What Are Mental Models?) The easiest way to describe them is that they're pat ...
- ui设计界面参数_参数化设计,可以更有效地设计用户界面
ui设计界面参数 User Interface Designers lacked proper design tools for years. Fortunately with the appeara ...
- python代码怎么写出色_如何写出更具有Python风格的代码,五分钟教会你!
我们都喜欢 Python,因为它让编程和理解变的更为简单.但是一不小心,我们就会忽略规则,以非 Pythonic 方式编写一堆垃圾代码,从而浪费 Python 这个出色的语言赋予我们的优雅.Pytho ...
- im和音视频开发哪个更好_如何阅读成为更好的开发者的方式
im和音视频开发哪个更好 by nolan grace 通过诺兰·格雷斯 如何阅读成为更好的开发者的方式 (How to read your way to becoming a better deve ...
最新文章
- Bootstrap学习遇到的role属性--- 无障碍网页应用属性
- json 反射java 实体_Java 将JSON反射到实体类
- Spring--总体架构
- 谷胱甘肽口服、舌下含服、NAC对照实验
- 导入开源的文件云存储平台-Seafile
- WebService工具类调用远程接口服务时java.io.IOException: Server returned HTTP response code: 500 for URL XXX
- Berg Insight:移动M2M连接将实现长足发展
- 深入理解HTTP一:网络基础TCP/IP
- Python3.6支付宝账单爬虫
- python3执行js之pyexecjs
- 中医大2020年7月网考计算机应用基础,2020年7月网络教育统考《计算机应用基础》操作系统应用模拟题试卷2...
- 社交产品分析:共同看片,微光
- linux查看nbu数据库命令,NBU基本常用命令
- android手机用户,ZDC:2011年Android手机用户使用行为研究报告
- 安卓机 input file图片上传无反应解决方案
- 从零开始学 Python 之环境搭建
- App 自动化解决方案 [开源项目] 基于 Appium 的 UI 自动化测试框架完美版
- 自己写的手机游戏脚本
- 转载:最值得反思:迟到太久不必到
- C盘爆满清理方法和不借助第三方工具完成C盘扩容(详细教程)
热门文章
- 牛客题霸 [ 数字在升序数组中出现的次数] C++题解/答案
- P3911 最小公倍数之和
- CF1580C Train Maintenance(分块)
- [CF/AT]各大网站网赛 体验部部长第一季度工作报告
- 数论一之定理证明——裴蜀/威尔逊/费马/扩展欧几里得/[扩展]欧拉/[扩展]中国剩余定理,欧拉函数,逆元,剩余系,筛法
- 51nod-动物与游戏【树链剖分,线段树】
- P4451-[国家集训队]整数的lqp拆分【生成函数,特征方程】
- Ch4302-IntervalGCD【线段树,树状数组,GCD】
- jzoj1082-合并果子【堆,贪心】
- Codeforces Global Round 11——E随机+线性基待补