1. 概要

この短い記事では、コントローラーで例外をスローする方法と、SpringMockMvcを使用してこれらの例外をテストする方法について説明します。

2. コントローラで例外をスローする

コントローラーから例外を起動する方法の学習を始めましょう。

コントローラから公開するサービスは、通常のJava関数であるかのように考えることができます。

@GetMapping("/exception/throw")
public void getException() throws Exception {
    throw new Exception("error");
}

それでは、このサービスを呼び出すとどうなるか見てみましょう。 まず、サービスの応答コードが500であり、これは内部サーバーエラーを意味します。

次に、次のような応答本文を受け取ります。

{
    "timestamp": 1592074599854,
    "status": 500,
    "error": "Internal Server Error",
    "message": "No message available",
    "trace": "java.lang.Exception
              at com.baeldung.controllers.ExceptionController.getException(ExceptionController.java:26)
              ..."
}

結論として、 RestController から例外をスローすると、サービス応答は自動的に500応答コードにマップされ、例外のスタックトレースが応答本文に含まれます。

3. 例外をHTTP応答コードにマッピングする

次に、例外を500以外のさまざまな応答コードにマッピングする方法を学習します。

これを実現するために、カスタム例外を作成し、Springによって提供されるResponseStatusアノテーションを使用します。 これらのカスタム例外を作成しましょう。

@ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadArgumentsException extends RuntimeException {

    public BadArgumentsException(String message) {
        super(message);
    }
}
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public class InternalException extends RuntimeException {

    public InternalException(String message) {
        super(message);
    }
}
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {

    public ResourceNotFoundException(String message) {
        super(message);
    }
}

2番目の最後のステップは、コントローラーに単純なサービスを作成して、これらの例外をスローすることです。

@GetMapping("/exception/{exception_id}")
public void getSpecificException(@PathVariable("exception_id") String pException) {
    if("not_found".equals(pException)) {
        throw new ResourceNotFoundException("resource not found");
    }
    else if("bad_arguments".equals(pException)) {
        throw new BadArgumentsException("bad arguments");
    }
    else {
        throw new InternalException("internal error");
    }
}

次に、マップしたさまざまな例外に対するサービスのさまざまな応答を見てみましょう。

  • not_found の場合、404の応答コードを受け取ります
  • bad_argumentsを指定すると、400の応答コードを受け取ります。
  • その他の値については、応答コードとして500を受け取ります

応答コードとは別に、前のセクションで受け取った応答本文と同じ形式の本文を受け取ります。

4. コントローラのテスト

最後に、コントローラーが正しい例外をスローしていることをテストする方法を確認します。

最初のステップは、テストクラスを作成し、MockMvcのインスタンスを作成することです。

@Autowired
private MockMvc mvc;

次に、サービスが受け取ることができる値ごとにテストケースを作成しましょう。

@Test
public void givenNotFound_whenGetSpecificException_thenNotFoundCode() throws Exception {
    String exceptionParam = "not_found";

    mvc.perform(get("/exception/{exception_id}", exceptionParam)
      .contentType(MediaType.APPLICATION_JSON))
      .andExpect(status().isNotFound())
      .andExpect(result -> assertTrue(result.getResolvedException() instanceof ResourceNotFoundException))
      .andExpect(result -> assertEquals("resource not found", result.getResolvedException().getMessage()));
}

@Test
public void givenBadArguments_whenGetSpecificException_thenBadRequest() throws Exception {
    String exceptionParam = "bad_arguments";

    mvc.perform(get("/exception/{exception_id}", exceptionParam)
      .contentType(MediaType.APPLICATION_JSON))
      .andExpect(status().isBadRequest())
      .andExpect(result -> assertTrue(result.getResolvedException() instanceof BadArgumentsException))
      .andExpect(result -> assertEquals("bad arguments", result.getResolvedException().getMessage()));
}

@Test
public void givenOther_whenGetSpecificException_thenInternalServerError() throws Exception {
    String exceptionParam = "dummy";

    mvc.perform(get("/exception/{exception_id}", exceptionParam)
      .contentType(MediaType.APPLICATION_JSON))
      .andExpect(status().isInternalServerError())
      .andExpect(result -> assertTrue(result.getResolvedException() instanceof InternalException))
      .andExpect(result -> assertEquals("internal error", result.getResolvedException().getMessage()));
}

これらのテストでは、応答コード、発生した例外のタイプ、およびその例外のメッセージが、各値に対して予期されたものであることを確認しています。

5. 結論

このチュートリアルでは、Spring RestController で例外を処理する方法と、公開された各サービスが予期された例外をスローしていることをテストする方法を学びました。

いつものように、記事の完全なソースコードはGitHubで入手できます。