Spring-boot: Spring Boot 2 Formatting LocalDate with WebTestClient

Created on 30 Apr 2018  路  2Comments  路  Source: spring-projects/spring-boot

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\"}");
}
invalid

Most helpful comment

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.

All 2 comments

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));
            }
        };
    }
}
Was this page helpful?
0 / 5 - 0 ratings