我们编写了一个小型的 Boot 应用程序,它在另一个REST端点上执行REST请求。
@RequestMapping("/api/v1")
@SpringBootApplication
@RestController
@Slf4j
public class Application
{
@Autowired
private WebClient webClient;
@RequestMapping(value = "/zyx", method = POST)
@ResponseBody
XyzApiResponse zyx(@RequestBody XyzApiRequest request, @RequestHeader HttpHeaders headers)
{
webClient.post()
.uri("/api/v1/someapi")
.accept(MediaType.APPLICATION_JSON)
.contentType(MediaType.APPLICATION_JSON)
.body(BodyInserters.fromObject(request.getData()))
.exchange()
.subscribeOn(Schedulers.elastic())
.flatMap(response ->
response.bodyToMono(XyzServiceResponse.class).map(r ->
{
if (r != null)
{
r.setStatus(response.statusCode().value());
}
if (!response.statusCode().is2xxSuccessful())
{
throw new ProcessResponseException(
"Bad status response code " + response.statusCode() + "!");
}
return r;
}))
.subscribe(body ->
{
// Do various things
}, throwable ->
{
// This section handles request errors
});
return XyzApiResponse.OK;
}
}
我们是Spring的新手,在为这个小代码段编写单元测试时遇到了麻烦。
有没有一种优雅的(React式的)方法来模拟webClient本身,或者启动一个webClient可以用作端点的模拟服务器?
9条答案
按热度按时间ao218c7q1#
我们通过提供一个定制的
ExchangeFunction
来实现这一点,该定制的ExchangeFunction
只向WebClientBuilder
返回我们想要的响应:如果我们想使用Mokcito来验证是否进行了调用,或者在类中的多个单元测试中重用WebClient,我们也可以模拟交换函数:
注意:如果在
when
调用中出现与发布者相关的空指针异常,则IDE可能导入了Mono.when
而不是Mockito.when
。g6baxovj2#
使用下面的方法,可以使用Mockito模拟WebClient进行如下调用:
或
模拟方法:
4ioopgfo3#
OkHttp团队可以使用MockWebServer,Spring团队基本上也在测试中使用它(至少他们是这样说here的),下面是一个参考源代码的示例:
根据Tim's blog post,我们考虑以下服务:
然后,可以按照以下方式设计测试(与原始测试相比,我更改了应如何使用
StepVerifier
在Reactor中测试异步链的方式):m4pnthwp4#
我使用WireMock进行集成测试,我认为它比OkHttp MockeWebServer好很多,支持的功能也更多,下面是一个简单的例子:
如果你真的想模仿它,我推荐JMockit。没有必要多次调用
when
,你可以像在测试代码中一样使用相同的调用。3vpjnl9f5#
Wire mocks适用于集成测试,而我认为单元测试不需要它。在进行单元测试时,我只想知道我的WebClient是否被调用了所需的参数。为此,您需要一个WebClient示例的mock。或者,您可以注入一个WebClientBuilder。
让我们考虑一个简单的方法,它像下面这样发出一个请求。
createSomething方法只接受一个String,为了简化示例,假设为Json,在URI上执行一个post请求,并返回输出响应主体(假设为String)。
可使用StepVerifier对该方法进行单元测试,如下所示。
请注意,“when”语句测试除请求主体之外的所有参数。即使其中一个参数不匹配,单元测试也会失败,从而Assert所有这些参数。然后,在单独的验证中Assert请求主体,并Assert“Mono”无法相等。然后使用步骤验证器验证结果。
然后,我们可以使用wire mock进行集成测试,如其他答案中所述,以查看该类是否正确连接,以及是否使用所需主体调用端点等。
tcbh2hod6#
我已经尝试了所有的解决方案在这里已经给出的答案。你的问题的答案是:这取决于你想做单元测试还是集成测试。
对于单元测试的目的,模拟WebClient本身太冗长,需要太多的代码。模拟Exchange函数更简单,更容易。对于这一点,可接受的答案必须是@Renette的解决方案。
对于集成测试来说,最好使用OkHttp MockWebServer。它使用起来很简单,也很灵活。使用服务器可以让你处理一些错误情况,否则你需要在单元测试中手动处理。
bqf10yzr7#
使用
spring-cloud-starter-contract-stub-runner
,您可以使用Wiremock模拟API响应。Here,您可以找到我在medium上描述的一个工作示例。AutoConfigureMockMvc
注解在测试之前启动一个Wiremock服务器,公开classpath:/mappings位置中的所有内容(可能是磁盘上的src/test/resources/mappings
)。下面是一个Map文件的例子。
balance.json
文件包含了你所需要的任何json内容。你也可以在静态配置文件中或编程中模拟响应延迟或失败。更多关于website的信息。pkwftd7m8#
我想使用webclient进行单元测试,但是mockito太复杂了,所以我创建了一个library,它可以用来在单元测试中构建mock webclient,它也可以在发送响应之前验证url,方法,头和请求主体。
mitkmikd9#
我强烈推荐使用Okhttp MockWebServer而不是mocking。原因是MockWebServer是一种干净得多的方法。
下面是可用于单元测试WebClient的代码模板。
要特别注意
initialize
方法,这是这里唯一需要技巧的地方。路径
/xyz
不是基本url,而是您的资源路径。您不需要将基本url告知MockWebServer。原因是,MockWebServer将使用某个随机端口在本地主机上启动服务器。如果您提供自己的基本url,则单元测试将失败。mockWebServer.url("/xyz")
这将为您提供基本url,即MockWebServer正在侦听的主机和端口,以及资源路径,例如
localhost:8999/xyz
。您需要使用此url创建WebClient。WebClient.create(httpUrl.toString())
这将创建一个WebClient,用于为您的单元测试调用MockWebServer。