When using WebTestClient in Spring Boot 2.0.1 I get different formatted dates depending on how I bind the test client see code below.
So how can I get the WebTestClient.bindToController to return LocalDate formatted as 2018-04-13? When I call WebTestClient.bindToServer() I get expected format.
Test below demonstrate the today value returned as different format depending on the how I bind the WebTestClient
@RestController
public class TodayController {
@GetMapping("/today")
public Map<String, Object> fetchToday() {
return ImmutableMap.of("today", LocalDate.now());
}
}
@ExtendWith({SpringExtension.class})
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
class TodayControllerTest {
@LocalServerPort
private int randomPort;
@Autowired
private TodayController controller;
@Test
void fetchTodayWebTestClientBoundToController() {
WebTestClient webTestClient = WebTestClient.bindToController(controller)
.configureClient()
.build();
webTestClient.get().uri("/today")
.exchange()
.expectBody()
.json("{\"today\":[2018,4,13]}");
}
@Test
void fetchTodayWebTestClientBoundToServer() {
WebTestClient webTestClient = WebTestClient.bindToServer()
.baseUrl("http://localhost:" + randomPort)
.build();
webTestClient.get().uri("/today")
.exchange()
.expectBody()
.json("{\"today\":\"2018-04-13\"}");
}
When you use bindToController you're testing an individual controller that runs in an isolated application context. In other words, you lose all of Boot's auto-configuration of WebFlux and Jackson. As a result, the ObjectMapper that's used doesn't write dates in the same way as it does when you call your application in the bindToServer case.
You can customise the codecs used in the bindToController case:
@Test
public void fetchTodayWebTestClientBoundToController() {
WebTestClient webTestClient = WebTestClient.bindToController(controller)
.httpMessageCodecs((configurer) -> {
CodecConfigurer.DefaultCodecs defaults = configurer.defaultCodecs();
defaults.jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper, new MimeType[0]));
defaults.jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper, new MimeType[0]));
})
.configureClient()
.build();
webTestClient.get().uri("/today")
.exchange()
.expectBody()
.json("{\"today\":\"2018-04-30\"}");
}
The above assumes that you have @Autowired your application's ObjectMapper into the test.
Although the issue is closed now, one could modify the solution slightly to utilize the @WebFluxTest capability:
@RunWith(SpringRunner.class)
@Import(TestConfiguration.class)
@WebFluxTest(TodayController.class)
public class TodayControllerTest {
@Autowired
WebTestClient webTestClient;
@Autowired
ObjectMapper objectMapper;
@Test
public void fetchTodayWebTestClientBoundToController() {
webTestClient.get().uri("/today")
.exchange()
.expectBody()
.json("{\"today\":\"2018-07-28\"}");
}
}
ObjectMapper is enabled via the JacksonAutoConfiguration.class which is already configured with the reasonable features by default and the message body codec confugurartion job is done by WebFluxConfigurer Bean. Important part here is really the configuration of http message codecs. I've tried the same with ExchangeStrategies, but without success.
@Configuration
@Import(JacksonAutoConfiguration.class)
@AutoConfigureAfter(JacksonAutoConfiguration.class)
public class TestConfiguration{
@Bean
WebFluxConfigurer webFluxConfigurer(ObjectMapper objectMapper) {
return new WebFluxConfigurer() {
@Override
public void configureHttpMessageCodecs(ServerCodecConfigurer configurer) {
configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper));
configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper));
}
};
}
}
Most helpful comment
When you use
bindToControlleryou're testing an individual controller that runs in an isolated application context. In other words, you lose all of Boot's auto-configuration of WebFlux and Jackson. As a result, theObjectMapperthat's used doesn't write dates in the same way as it does when you call your application in thebindToServercase.You can customise the codecs used in the
bindToControllercase:The above assumes that you have
@Autowiredyour application'sObjectMapperinto the test.