Essa é mais uma dica de Testes Unitários!
Com mais algumas dicas ~escondidas~!
Vamos a um exemplo simples de um endpoint POST com body e algumas validações:
@RestController @RequestMapping("v1/persons") public class PersonController { @PostMapping @ResponseStatus(HttpStatus.CREATED) public void create(@RequestBody @Valid final PersonBody body) { //impl } @With @Builder public record PersonBody(@NotBlank @Size(max = 10) String name, @NotNull @Past LocalDate birthdate) { } }
Os testes básicos válidos para esse endpoint são:
- o de sucesso, com todos os campos válidos
- os de erro, com todas as possibilidades de campos inválidos
Em uma contagem rápida, seria necessário UM de sucesso (CREATED) e cerca de SETE de erro (BAD_REQUEST).
O de sucesso é simples:
@WebMvcTest(PersonController.class) class PersonControllerTest { private static final String URL = "/v1/persons"; @Autowired private MockMvc mockMvc; @Autowired private ObjectMapper objectMapper; @Nested class WhenPost { private static PersonBody validBody; static { validBody = PersonBody.builder() .name("Igor") .birthdate(LocalDate.now().minusDays(1)) .build(); } @Test @SneakyThrows void shouldReturnCreated() { mockMvc.perform( post(URL) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(validBody)) ).andExpect(status().isCreated()); } } }
Se desejar testar o body/contrato esperado também é possível:
.andExpect(jsonPath("$.<campo>").value(<expected>))
Porém o foco aqui são os testes de Bad Request (400).
Seria necessário criar UM método para cada @Test e possibilidade de erro do payload enviado:
@Test @SneakyThrows void shouldReturnBadRequestBecauseNameIsNull() { mockMvc.perform( post(URL) .contentType(MediaType.APPLICATION_JSON) .content( objectMapper.writeValueAsString(validBody.withName(null)) ) ).andExpect(status().isBadRequest()); }
Se tornaria maçante criar as SETE possibilidades de erros (isso que é um body simples, com apenas 2 campos, imagine payloads mais complexos).
E aqui vai uma solução muito interessante: @ParameterizedTest !
@ParameterizedTest @MethodSource("badBodies") @SneakyThrows void shouldReturnBadRequest(final PersonBody body) { mockMvc.perform( post(URL) .contentType(MediaType.APPLICATION_JSON) .content(objectMapper.writeValueAsString(body)) ).andExpect(status().isBadRequest()); } static Stream<Arguments> badBodies() { return Stream.of( Arguments.of(validBody.withName(null)), Arguments.of(validBody.withName("")), Arguments.of(validBody.withName(" ")), Arguments.of(validBody.withName("112233445566")), Arguments.of(validBody.withBirthdate(null)), Arguments.of(validBody.withBirthdate(LocalDate.now())), Arguments.of(validBody.withBirthdate(LocalDate.now().plusDays(1))) ); } }
Para testes com MÚLTIPLAS situações que devem ter o MESMO resultado os testes parametrizados são perfeitos! Para testes em endpoints que possuam validações o @ParameterizedTest serve como uma luva!
Podem notar que utilizei o @MethodSource
para alimentar o argumento do teste, existem outras formas de prover o argumento (pacote: org.junit.jupiter.params.provider
).
"Aaaah, além do 400 eu quero testar a mensagem do erro, se é a correta para aquele campo ou não." Essa dica será em outro post!
Top comments (0)