Test delle eccezioni con Spring MockMvc

1. Panoramica

In questo breve articolo, vedremo come dovrebbero essere lanciate le eccezioni nei nostri controller e come testare queste eccezioni usando Spring MockMvc.

2. Lanciare eccezioni nei controller

Cominciamo ad imparare come lanciare un'eccezione da un controller .

Possiamo pensare ai servizi che esponiamo da un controller allo stesso modo come se fossero normali funzioni Java:

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

Ora, vediamo cosa succede quando chiamiamo questo servizio. Innanzitutto, noteremo che il codice di risposta del servizio è 500 che significa Errore interno del server.

In secondo luogo, riceviamo un corpo di risposta come questo:

{ "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) ..." }

In conclusione, quando lanciamo un'eccezione da un RestController , la risposta del servizio viene automaticamente mappata a un codice di risposta 500 e la traccia dello stack dell'eccezione è inclusa nel corpo della risposta.

3. Mappatura delle eccezioni ai codici di risposta HTTP

Ora impareremo come mappare le nostre eccezioni a diversi codici di risposta diversi da 500.

Per ottenere ciò, creeremo eccezioni personalizzate e utilizzeremo l' annotazione ResponseStatus fornita da Spring. Creiamo quelle eccezioni personalizzate:

@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); } }

Il secondo e ultimo passaggio consiste nel creare un semplice servizio nel nostro controller per generare queste eccezioni:

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

Vediamo ora le diverse risposte del servizio per le diverse eccezioni che abbiamo mappato:

  • Per not_found , riceviamo un codice di risposta 404
  • Dato il valore bad_arguments , riceviamo un codice di risposta di 400
  • Per qualsiasi altro valore, riceviamo ancora 500 come codice di risposta

Oltre ai codici di risposta, riceveremo un corpo con lo stesso formato del corpo della risposta ricevuto nella sezione precedente.

4. Testare i nostri controller

Infine, vedremo come verificare che il nostro controller stia generando le eccezioni corrette .

Il primo passo è creare una classe di test e creare un'istanza di MockMvc :

@Autowired private MockMvc mvc; 

Successivamente, creiamo i casi di test per ciascuno dei valori che il nostro servizio può ricevere:

@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())); }

Con questi test, stiamo verificando che il codice di risposta, il tipo di eccezione sollevata ei messaggi di tali eccezioni siano quelli previsti per ciascuno dei valori.

5. conclusione

In questo tutorial, abbiamo imparato come gestire le eccezioni nel nostro Spring RestController e come testare che ogni servizio esposto stia generando le eccezioni previste.

Come sempre, il codice sorgente completo dell'articolo è disponibile su GitHub.