Spring WebClient的单元测试

WebClient引用其Java文档是Spring Framework的

非阻塞,反应式客户端执行HTTP请求,通过底层HTTP客户端库(如Reactor Netty)公开流利的,反应式API

在我当前的项目中,我广泛使用WebClient进行服务到服务的调用,并发现它是一个了不起的API,并且我喜欢使用Fluent接口。

考虑一个返回“城市”列表的远程服务。 使用WebClient的代码如下所示:

 ...  import org.springframework.http.MediaType  import org.springframework.web.reactive.function.client.WebClient  import org.springframework.web.reactive.function.client.bodyToFlux  import org.springframework.web.util.UriComponentsBuilder  import reactor.core.publisher.Flux  import java.net.URI  CitiesClient( class CitiesClient( private val webClientBuilder: WebClient.Builder, private val citiesBaseUrl: String  ) { fun getCities(): Flux<City> { val buildUri: URI = UriComponentsBuilder .fromUriString(citiesBaseUrl) .path( "/cities" ) .build() .encode() .toUri() val webClient: WebClient = this .webClientBuilder.build() return webClient.get() .uri(buildUri) .accept(MediaType.APPLICATION_JSON) .exchange() .flatMapMany { clientResponse -> clientResponse.bodyToFlux<City>() } }  } 

但是,很难测试使用WebClient的客户端。 在本文中,我将介绍使用WebClient和干净的解决方案测试客户端的挑战。

模拟WebClient的挑战

要对“ CitiesClient”类进行有效的单元测试,将需要模拟WebClient以及流利的接口链中的每个方法调用,包括:

 val mockWebClientBuilder: WebClient.Builder = mock()  val mockWebClient: WebClient = mock()  whenever(mockWebClientBuilder.build()).thenReturn(mockWebClient)  val mockRequestSpec: WebClient.RequestBodyUriSpec = mock()  whenever(mockWebClient.get()).thenReturn(mockRequestSpec)  val mockRequestBodySpec: WebClient.RequestBodySpec = mock()  whenever(mockRequestSpec.uri(any<URI>())).thenReturn(mockRequestBodySpec)  whenever(mockRequestBodySpec.accept(any())).thenReturn(mockRequestBodySpec)  val citiesJson: String = this .javaClass.getResource( "/sample-cities.json" ).readText()  val clientResponse: ClientResponse = ClientResponse .create(HttpStatus.OK) .header( "Content-Type" , "application/json" ) .body(citiesJson).build()  whenever(mockRequestBodySpec.exchange()).thenReturn(Mono.just(clientResponse))  val citiesClient = CitiesClient(mockWebClientBuilder, " http://somebaseurl " )  val cities: Flux<City> = citiesClient.getCities() 

这使得测试非常不稳定,因为调用顺序的任何更改都将导致需要记录新的模拟。

使用真实端点进行测试

一种行之有效的方法是启动行为类似于客户端目标的真实服务器。 okhttp库和WireMock中的两个模拟服务器运行得很好,它们是模拟服务器。 Wiremock的示例如下所示:

 import com.github.tomakehurst.wiremock.WireMockServer  import com.github.tomakehurst.wiremock.client.WireMock  import com.github.tomakehurst.wiremock.core.WireMockConfiguration  import org.bk.samples.model.City  import org.junit.jupiter.api.AfterAll  import org.junit.jupiter.api.BeforeAll  import org.junit.jupiter.api.Test  import org.springframework.http.HttpStatus  import org.springframework.web.reactive.function.client.WebClient  import reactor.core.publisher.Flux  import reactor.test.StepVerifier  WiremockWebClientTest { class WiremockWebClientTest { @Test fun testARemoteCall() { val citiesJson = this .javaClass.getResource( "/sample-cities.json" ).readText() WIREMOCK_SERVER.stubFor(WireMock.get(WireMock.urlMatching( "/cities" )) .withHeader( "Accept" , WireMock.equalTo( "application/json" )) .willReturn(WireMock.aResponse() .withStatus(HttpStatus.OK.value()) .withHeader( "Content-Type" , "application/json" ) .withBody(citiesJson))) val citiesClient = CitiesClient(WebClient.builder(), " http://localhost: ${WIREMOCK_SERVER.port()}" ) val cities: Flux<City> = citiesClient.getCities()         StepVerifier .create(cities) .expectNext(City(1L, "Portland" , "USA" , 1_600_000L)) .expectNext(City(2L, "Seattle" , "USA" , 3_200_000L)) .expectNext(City(3L, "SFO" , "USA" , 6_400_000L)) .expectComplete() .verify() } companion object { private val WIREMOCK_SERVER = WireMockServer(WireMockConfiguration.wireMockConfig().dynamicPort()) @BeforeAll @JvmStatic fun beforeAll() { WIREMOCK_SERVER.start() } @AfterAll @JvmStatic fun afterAll() { WIREMOCK_SERVER.stop() } }  } 

在这里,服务器是通过随机端口启动的,然后注入行为,然后针对该服务器测试客户端并进行验证。 这种方法有效,并且在模拟此行为时不会与WebClient的内部混淆,但是从技术上讲,这是一个集成测试,并且比纯单元测试执行起来要慢。

通过使远程呼叫短路来进行单元测试

我最近使用的一种方法是使用ExchangeFunction短路远程调用。 ExchangeFunction代表进行远程调用的实际机制,可以用一种以测试期望的方式响应的方式代替ExchangeFunction:

 import org.junit.jupiter.api.Test  import org.springframework.http.HttpStatus  import org.springframework.web.reactive.function.client.ClientResponse  import org.springframework.web.reactive.function.client.ExchangeFunction  import org.springframework.web.reactive.function.client.WebClient  import reactor.core.publisher.Flux  import reactor.core.publisher.Mono  import reactor.test.StepVerifier  CitiesWebClientTest { class CitiesWebClientTest { @Test fun testCleanResponse() { val citiesJson: String = this .javaClass.getResource( "/sample-cities.json" ).readText() val clientResponse: ClientResponse = ClientResponse .create(HttpStatus.OK) .header( "Content-Type" , "application/json" ) .body(citiesJson).build() val shortCircuitingExchangeFunction = ExchangeFunction { Mono.just(clientResponse) } val webClientBuilder: WebClient.Builder = WebClient.builder().exchangeFunction(shortCircuitingExchangeFunction) val citiesClient = CitiesClient(webClientBuilder, " http://somebaseurl " ) val cities: Flux<City> = citiesClient.getCities() StepVerifier .create(cities) .expectNext(City(1L, "Portland" , "USA" , 1_600_000L)) .expectNext(City(2L, "Seattle" , "USA" , 3_200_000L)) .expectNext(City(3L, "SFO" , "USA" , 6_400_000L)) .expectComplete() .verify() }  } 

WebClient注入了ExchangeFunction,该函数仅返回具有远程服务器预期行为的响应。 这使整个远程呼叫短路,并允许对客户端进行全面测试。 这种方法取决于对WebClient内部的一点了解。 虽然这是一个不错的妥协,但是它的运行速度比使用WireMock进行的测试要快得多。

但是这种方法不是原始的,我已经基于用于测试WebClient本身的一些测试来建立此测试,例如, 此处的

结论

我个人更喜欢最后一种方法,它使我能够为使用WebClient进行远程调用的客户端编写相当全面的单元测试。 我的项目具有完整的工作样本, 在这里 。

翻译自: https://www.javacodegeeks.com/2019/09/unit-test-springs-webclient.html

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

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

相关文章

以太网的光猫和光纤的光猫有什么区别吗?

光调制解调器&#xff0c;光猫也称为单端口光端机&#xff0c;是针对特殊用户环境而研发的一种三件一套的光纤传输设备。该设备采用大规模集成芯片&#xff0c;电路简单&#xff0c;功耗低&#xff0c;可靠性高&#xff0c;具有完整的告警状态指示和完善的网管功能。光猫可以分…

SFP光模块基本概念及使用注意事项详解

光模块(optical module)由光电子器件、功能电路和光接口等组成&#xff0c;光电子器件包括发射和接收两部分。简单的说&#xff0c;光模块的作用就是光电转换&#xff0c;发送端把电信号转换成光信号&#xff0c;通过光纤传送后&#xff0c;接收端再把光信号转换成电信号。现在…

Java中的状态设计模式

在本教程中&#xff0c;我们将探讨另一种流行的行为设计模式-状态设计模式。 当我们使用可以存在于多个状态的对象时&#xff0c;状态设计模式的知识变得非常有用。 当对象的行为取决于其当前状态时&#xff0c;我们应该主要使用它。 这种模式有助于我们避免在该类的方法中对对…

串口服务器工作方式及常见异常故障问题排除方法介绍

串口设备联网服务器就像一台带CPU、实时操作系统和TCP/IP协议的微型电脑&#xff0c;方便在串口和网络设备中传输数据。您可以在世界任何位置通过网络&#xff0c;用您的计算机来存取&#xff0c;管理和配置远程的设备。但是我们在实际使用串口服务器的过程中&#xff0c;难免会…

E1立体声卡侬头(XLR) 音频编解码器产品介绍

FCA系列音频编解码器是一种采用数字处理芯片及大规模FPGA、数字编解码转换和时钟恢复技术方法设计的基于E1(2M)通道传输立体声广播信号的广播传送设备系统&#xff0c;它可以借助目前成熟可靠的E1传输系统网&#xff0c;建立起数字立体声广播传送系统网络&#xff0c;实现广播传…

E1视音频编解码器应用方案详细说明

E1音频编解码器&#xff0c;是采用数字处理芯片及大规模FPGA、数字编解码转换和时钟恢复技术方法设计的。适用于广播节目源传输的设备。音频编码器是将输入的立体声音频信号&#xff0c;经A/D数字编解码变换或数据格式转换后&#xff0c;重新成帧&#xff0c;形成串行数据流&am…

java请求接口示例_Java 8:功能接口示例

java请求接口示例为了支持Java 8中的lambda表达式&#xff0c;他们引入了Functional Interfaces。 具有单一抽象方法的接口可以称为功能接口。 Runnable&#xff0c;Comparator&#xff0c;Cloneable是功能接口的一些示例。 我们可以使用Lambda表达式实现这些功能接口。 例如…

Java中的外观设计模式

立面是指建筑物的外观。 当穿过街道时&#xff0c;我们所看到的只是建筑物的外观。 该图面抽象了建筑物的所有复杂实施细节。 同样&#xff0c; 外观设计模式旨在为子系统中的一组接口提供统一的接口。 这个统一的接口对客户端隐藏了子系统的复杂性。 它属于结构模式类别。 J…

串口服务器介绍及产品特点详解

串口服务器提供串口转网络功能&#xff0c;能够将RS-232/485/422串口转换成TCP/IP网络接口&#xff0c;实现RS-232/485/422串口与TCP/IP网络接口的数据双向透明传输。使得串口设备能够立即具备TCP/IP网络接口功能&#xff0c;连接网络进行数据通信&#xff0c;极大的扩展串口设…

串口服务器应用领域及应用方案详解

串口服务器提供串口转网络功能&#xff0c;使得串口设备能够立即具备TCP/IP网络接口功能&#xff0c;连接网络进行数据通信&#xff0c;极大的扩展串口设备的通信距离&#xff0c;应用领域非常广泛。接下来我们就来为大家详细介绍下串口服务器的应用领域及应用方案&#xff0c;…

将对象转换为Map并返回

在大型企业应用程序中&#xff0c;有时我们需要将数据对象与Map相互转换。 通常&#xff0c;这是特殊序列化的中间步骤。 如果可以使用某种标准&#xff0c;则最好使用该标准&#xff0c;但是很多时候&#xff0c;一些首席架构师所设想的体系结构&#xff0c;严格的环境或某些类…

多串口服务器的工作方式及接线示意图介绍

串口服务器提供串口转网络功能&#xff0c;能够将RS-232/485/422串口转换成TCP/IP网络接口&#xff0c;实现RS-232/485/422串口与TCP/IP网络接口的数据双向透明传输。那么&#xff0c;多串口服务器是怎么工作&#xff0c;怎么接线的&#xff1f;接下来就由飞畅科技小编一起来为…

多路串口服务器的应用及应用范围介绍

串口服务器的应用领域很是广泛&#xff0c;主要应用在门禁系统、考勤系统、售贩系统、POS系统、楼宇自控系统、自助银行系统、电信机房监控、电力监控等。接下来就由飞畅科技的小编来为大家详细介绍下多路串口服务器的应用及应用范围&#xff0c;感兴趣的朋友就一起来看看吧&am…

javafx 调用接口_JavaFX技巧3:使用回调接口

javafx 调用接口作为UI框架开发人员&#xff0c;提供自定义控件外观和行为的方法是我工作的一部分。 在许多情况下&#xff0c;这是通过允许框架用户在控件上注册工厂来完成的。 过去&#xff0c;我会为此创建一个工厂接口&#xff0c;并在框架内提供一个或多个默认实现。 这些…

工业串口服务器如何使用

串口联网服务器让传统的RS-232/422/485设备立即联网&#xff0c;利用基于TCP/IP的串口数据流传输的实现来控制管理的设备硬件是专为串口转以太网设计连接的桥梁。那么&#xff0c;我们该如何正确使用工业串口服务器呢&#xff1f;接下来我们就跟随飞畅科技的小编一起来看看吧&a…

JMetro 5.5版发布

JMetro 5.5版刚刚发布。 进行了重大更新&#xff0c;其中添加了一些错误修复&#xff0c;样式和功能。 一个新的主题测试器示例应用程序也已添加到示例子项目中&#xff0c;该应用程序测试了几项内容&#xff0c;例如控件之间的对齐。 我将尽量使这篇文章简短&#xff0c;因为…

关于工业级RS485串口服务器的组网方式详解

工业级串口服务器提供串口转网络功能&#xff0c;是实现串口&#xff08;TTL串口或RS232/RS485/RS422&#xff09;数据与TCP/IP协议数据相互转换实现数据通过网络传输的工业互联通讯设备。通过连接多个串口设备并将串口数据流进行选择和处理&#xff0c;将串口数据转换为以太网…

什么是四路串口服务器?

四路串口RS232/RS422/RS485设备联网服务器&#xff08;以下简称&#xff1a;串口服务器&#xff09;是由杭州飞畅科技自主研发的串口设备联网产品&#xff0c;其中RS232/RS422/RS485可以任意组合使用(客户可定制&#xff09;。那么&#xff0c;什么是四路串口服务器&#xff1f…

Java中的代理设计模式

代理对象或代理对象为另一个对象提供占位符&#xff0c;以控制对该对象的访问。 代理充当原始对象的轻量级版本或简化版本。 它支持与原始对象相同的操作&#xff0c;但可以将那些请求委托给原始对象以实现它们。 代理设计模式是一种结构模式&#xff0c;其中代理类包装了实际…

光模块简介、结构介绍及分类详解

光模块(optical module)由光电子器件、功能电路和光接口等组成&#xff0c;光电子器件包括发射和接收两部分。简单的说&#xff0c;光模块的作用就是光电转换&#xff0c;发送端把电信号转换成光信号&#xff0c;通过光纤传送后&#xff0c;接收端再把光信号转换成电信号。接下…