我们编写了一个小型Spring Boot REST应用程序,它在另一个RESTendpoint上执行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新手,在为这个小代码段编写单元测试时遇到了困难。
是否有一种优雅的(反应式)方式来模拟webClient本身或启动webClient可以用作endpoint的模拟服务器?
OkHttp团队可以使用MockWebServer。基本上,Spring团队也将其用于测试(至少他们在这里是这样说的)。以下是一个参考源的示例:
根据Tim的博客文章,让我们考虑一下我们有以下服务:
class ApiCaller {
private WebClient webClient;
ApiCaller(WebClient webClient) {
this.webClient = webClient;
}
Mono<SimpleResponseDto> callApi() {
return webClient.put()
.uri("/api/resource")
.contentType(MediaType.APPLICATION_JSON)
.header("Authorization", "customAuth")
.syncBody(new SimpleRequestDto())
.retrieve()
.bodyToMono(SimpleResponseDto.class);
}
}
然后可以按照以下方式设计测试(与origin相比,我改变了异步链在Reactor中使用StepVerifier进行测试的方式):
class ApiCallerTest {
private final MockWebServer mockWebServer = new MockWebServer();
private final ApiCaller apiCaller = new ApiCaller(WebClient.create(mockWebServer.url("/").toString()));
@AfterEach
void tearDown() throws IOException {
mockWebServer.shutdown();
}
@Test
void call() throws InterruptedException {
mockWebServer.enqueue(new MockResponse().setResponseCode(200)
.setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
.setBody("{\"y\": \"value for y\", \"z\": 789}")
);
//Asserting response
StepVerifier.create(apiCaller.callApi())
.assertNext(res -> {
assertNotNull(res);
assertEquals("value for y", res.getY());
assertEquals("789", res.getZ());
})
.verifyComplete();
//Asserting request
RecordedRequest recordedRequest = mockWebServer.takeRequest();
//use method provided by MockWebServer to assert the request header
recordedRequest.getHeader("Authorization").equals("customAuth");
DocumentContext context = >JsonPath.parse(recordedRequest.getBody().inputStream());
//use JsonPath library to assert the request body
assertThat(context, isJson(allOf(
withJsonPath("$.a", is("value1")),
withJsonPath("$.b", is(123))
)));
}
}
使用以下方法,可以使用Mockito模拟WebClient进行以下调用:
webClient
.get()
.uri(url)
.header(headerName, headerValue)
.retrieve()
.bodyToMono(String.class);
或
webClient
.get()
.uri(url)
.headers(hs -> hs.addAll(headers));
.retrieve()
.bodyToMono(String.class);
模拟方法:
private static WebClient getWebClientMock(final String resp) {
final var mock = Mockito.mock(WebClient.class);
final var uriSpecMock = Mockito.mock(WebClient.RequestHeadersUriSpec.class);
final var headersSpecMock = Mockito.mock(WebClient.RequestHeadersSpec.class);
final var responseSpecMock = Mockito.mock(WebClient.ResponseSpec.class);
when(mock.get()).thenReturn(uriSpecMock);
when(uriSpecMock.uri(ArgumentMatchers.<String>notNull())).thenReturn(headersSpecMock);
when(headersSpecMock.header(notNull(), notNull())).thenReturn(headersSpecMock);
when(headersSpecMock.headers(notNull())).thenReturn(headersSpecMock);
when(headersSpecMock.retrieve()).thenReturn(responseSpecMock);
when(responseSpecMock.bodyToMono(ArgumentMatchers.<Class<String>>notNull()))
.thenReturn(Mono.just(resp));
return mock;
}
我们通过提供一个自定义的ExchangeFunction来实现这一点,该函数只需将我们想要的响应返回给WebClient Builder:
webClient = WebClient.builder()
.exchangeFunction(clientRequest ->
Mono.just(ClientResponse.create(HttpStatus.OK)
.header("content-type", "application/json")
.body("{ \"key\" : \"value\"}")
.build())
).build();
myHttpService = new MyHttpService(webClient);
Map<String, String> result = myHttpService.callService().block();
// Do assertions here
如果我们想使用Mokcito来验证是否进行了调用或在类中的多个单元测试中重用WebClient,我们还可以模拟交换函数:
@Mock
private ExchangeFunction exchangeFunction;
@BeforeEach
void init() {
WebClient webClient = WebClient.builder()
.exchangeFunction(exchangeFunction)
.build();
myHttpService = new MyHttpService(webClient);
}
@Test
void callService() {
when(exchangeFunction.exchange(any(ClientRequest.class)))
.thenReturn(buildMockResponse());
Map<String, String> result = myHttpService.callService().block();
verify(exchangeFunction).exchange(any());
// Do assertions here
}
注意:如果在调用时得到与发布者相关的空指针异常,则IDE可能已导入Mono。而不是Mockito。当 。
来源:
CacheController.Somemethod();但是当submit方法被称为submit方法时,它会创建一个线程和cacheController.somemethod();从不调用测试类。
当我运行测试时,我可以做什么来更改时间
null 我看过其他几个类似的问题,但没有一个有帮助: 模拟包含对SQLiteOpenHelper引用的类时出错 使用mockito库在java中模拟最终类-我在导入PowerMock时遇到了很多问题 如何用mockito模拟最后一个类-我已经添加了依赖项,并用行创建了文件,正如答案中所建议的那样,我仍然得到同样的错误。我还尝试了建议的答案,但这给了我一个‘没有足够的信息来推断类型变量t'erro
我的要求是测试这段代码,更重要的是测试序列化器,因此,给出一个JSON片段,我如何测试所有的值都正确地通过商人的实例。 我不知道RestTemplate使用哪个序列化器将JSON序列化为对象。
我知道Dan North设计BDD的意图之一是将词汇表从复杂的测试域中移开。然而,在实现由外到内的方法时,我们似乎仍然需要对模仿行为(或者,如果您愿意的话)有一些了解。North在这个视频中建议,如果我从最外层的域对象开始,然后向内工作,我会在发现合作者时模仿它们,然后用适当的实现替换它们。所以最后,我以一组端到端测试结束。 Martin Fowler在这篇博客文章中定义了TDD的两个阵营:“古典
问题内容: 在我的测试案例中,我需要测试时间敏感的方法,在该方法中,我们使用的是Java 8类LocalDate,而 不是Joda 。 运行测试时,我该如何更改时间? 问题答案: 在您的代码中,将替换为。 然后,您可以通过生产并使用固定时钟进行测试。 这是一个例子: 首先,注入。如果您使用的是Spring Boot,请执行以下操作: 其次,输入您的代码: 现在,在单元测试类中:
它返回null,而不是预期的客户端,对象类的工作正常。我只想写测试。我是漏掉了什么还是做错了测试?谢谢你的指导。
问题内容: 在我的代码中,我有这样的东西: 如何在junit测试中“模拟”它以返回特定日期? 问题答案: 就我所知,您有三个明智的选择: 将实例插入您当天设置的任何方法/类中。 使用JodaTime而不是。这不是一个选择,而是一个建议,因为JodaTime将使您的生活更加轻松。您仍然需要将此时间注入该方法中。 包装在一些界面中,可以让您获取时间。然后,您只需模拟该接口并使其返回常量即可。