0
0

More than 3 years have passed since last update.

Spring Bootで MockMvcを使う際に、@RequestParamでLocalDateTimeを受け取る

Last updated at Posted at 2021-08-24

やりたかったこと

Spring SecurityでOAuth2認証を有効にしたRestControllerにおいて、引数にLocalDateTimeを取るAPIを作成。

そしてこれをMockMvcでテストしたい。

プロダクションコード

TestController.java
  @GetMapping("/test/date")
  public ResponseEntity<?> paramCheck(
          @RequestParam("date") LocalDateTime date,
          @AuthenticationPrincipal DefaultOAuth2User defaultOAuth2User) {

    // 引数があればOK, なければBAD REQUEST
    if (date != null) {
      return new ResponseEntity<HealthCheckResponse>(
          HealthCheckResponse.builder()
              .randomId(UUID.randomUUID().toString())
              .nowDate(date)
              .build(),
          HttpStatus.OK);
    } else {
      return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
    }
  }

テストコード(修正前)

パラメータに渡すDefaultOAuth2Userを上書きするため、standaloneSetupで初期化を実施。

TestControllerTest.java

  @Autowired
  private TestController targetController;

  private MockMvc mockMvc;

  @BeforeEach
  void setUp() {
    mockMvc =
        MockMvcBuilders
            .standaloneSetup(targetController)
            .alwaysDo(print())
             // OAuthの認証情報を更新するための処理を追加
            .setCustomArgumentResolvers(new HandlerMethodArgumentResolver() {})
            .build();
    // Mock
    MockitoAnnotations.openMocks(this);
  }

  @Test
  void callTest() {
    var testDate = LocalDateTime.of(2021, 03, 03, 12, 13, 14);
    var sendDate = testDate.toString();

    mockMvc
        .perform(
            get("/test/date")
                .contentType(MediaType.APPLICATION_JSON)
                .param("date", sendDate)
                .characterEncoding("UTF-8")
        .andExpect(status().isOk());
  }

実行してみた結果

Resolved Exception:
             Type = org.springframework.web.method.annotation.MethodArgumentTypeMismatchException

どうやら、String -> LocalDateTimeの変換で失敗していた模様。

何が起きたのか・・・?

bootRunで起動した時にはデフォルトの設定がロードされて正常に変換されているので、MockMvcの問題であることは確実。

LocalDateTimeを引数に取る場合、内部的にSpringさんがよしなにして変換していると考えられるのだが、
MockMvcをstandaloneモードで初期化していたため、Spring Bootで自動設定している内容が反映されていないと仮定。

確かに、controllerAdviceも自分で指定しないといけないので、
この辺りの設定は全て手動でやる必要がありそう。

やるべきこと

  • Requestの処理をbootRunの時に合わせたConfigurationを追加する
  • Responseの処理でJacksonを使用するため、その設定を反映する

調べてなんとなくわかったこと

MockMvcの初期化で、下記をそれぞれ指定すれば良さげ。

  • setConversionService(リクエスト処理)
  • setMessageConverters(レスポンス処理)

それぞれに導入する値はSpringBootがComponentとして管理しているので、Autowiredすれば自動で注入される(と仮定して実装)

ConversionServiceの指定クラス

日付などの設定をするために利用しているのでこれを使用。

WebConversionService

DIする前提なので、スーパークラスであるFormattingConversionServiceを指定した方がもしかしたら確実かもしれない。

MessageConverterの指定クラス

Jackson用のが見つかったのでそれをそのまま使用。

MappingJackson2HttpMessageConverter

修正後コード

MockMvcの初期化をガッツリ変更して対応。

  private MockMvc mockMvc;

  @BeforeEach
  void setUp(
      /** リクエストパラメータを処理するための共通サービス */
      @Autowired WebConversionService conversionService,
      /** Responseの処理をするためのコンバーター */
      @Autowired MappingJackson2HttpMessageConverter mappingJackson2HttpMessageConverter) {
    mockMvc =
        MockMvcBuilders.standaloneSetup(targetController)
            .alwaysDo(print())
            // 認証情報の上書き(処理省略)
            .setCustomArgumentResolvers(new HandlerMethodArgumentResolver() {})
            // パラメータの処理で共通処理を利用するようにする
            .setConversionService(conversionService)
            // レスポンス処理でJacksonを使うようにする
            .setMessageConverters(mappingJackson2HttpMessageConverter)
            // エラーハンドラーの有効化(自前のAdviceがあるなら、足さないとスルーされる)
            .setControllerAdvice(new CommonHandlerAdvice())
            .build();
    // Mock
    MockitoAnnotations.openMocks(this);
  }

実施したらうまくいったのでヨシ!

さいごに

SpringBootマジで全然わからん

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0