Tester les exceptions avec Spring MockMvc

1. Vue d'ensemble

Dans ce court article, nous verrons comment les exceptions doivent être levées dans nos contrôleurs et comment tester ces exceptions à l'aide de Spring MockMvc.

2. Lancer des exceptions dans les contrôleurs

Commençons par apprendre à lancer une exception à partir d'un contrôleur .

Nous pouvons penser aux services que nous exposons à partir d'un contrôleur de la même manière que s'il s'agissait de fonctions Java normales:

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

Voyons maintenant ce qui se passe lorsque nous appelons ce service. Tout d'abord, nous remarquerons que le code de réponse du service est 500, ce qui signifie une erreur interne du serveur.

Deuxièmement, nous recevons un corps de réponse comme celui-ci:

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

En conclusion, lorsque nous lançons une exception à partir d'un RestController , la réponse du service est automatiquement mappée à un code de réponse 500 et la trace de pile de l'exception est incluse dans le corps de la réponse.

3. Mappage des exceptions aux codes de réponse HTTP

Nous allons maintenant apprendre à mapper nos exceptions à différents codes de réponse autres que 500.

Pour ce faire, nous allons créer des exceptions personnalisées et utiliser l' annotation ResponseStatus fournie par Spring. Créons ces exceptions personnalisées:

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

La deuxième et dernière étape consiste à créer un service simple dans notre contrôleur pour lever ces exceptions:

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

Voyons maintenant les différentes réponses du service pour les différentes exceptions que nous avons mappées:

  • Pour not_found , nous recevons un code de réponse de 404
  • Étant donné la valeur bad_arguments , nous recevons un code de réponse de 400
  • Pour toute autre valeur, nous recevons toujours 500 comme code de réponse

Outre les codes de réponse, nous recevrons un corps avec le même format que le corps de réponse reçu dans la section précédente.

4. Test de nos contrôleurs

Enfin, nous allons voir comment tester que notre contrôleur lève les exceptions correctes .

La première étape consiste à créer une classe de test et à créer une instance de MockMvc :

@Autowired private MockMvc mvc; 

Ensuite, créons les cas de test pour chacune des valeurs que notre service peut recevoir:

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

Avec ces tests, nous vérifions que le code de réponse, le type d'exception levée et les messages de ces exceptions sont ceux attendus pour chacune des valeurs.

5. Conclusion

Dans ce didacticiel, nous avons appris à gérer les exceptions dans nos Spring RestController et à tester que chaque service exposé lève les exceptions attendues.

Comme toujours, le code source complet de l'article est disponible dans GitHub.