DEV Community

Igor Rudel
Igor Rudel

Posted on

Dica Java: @ParameterizedTest #008

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) { } } 
Enter fullscreen mode Exit fullscreen mode

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()); } } } 
Enter fullscreen mode Exit fullscreen mode

Se desejar testar o body/contrato esperado também é possível:

.andExpect(jsonPath("$.<campo>").value(<expected>)) 
Enter fullscreen mode Exit fullscreen mode

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()); } 
Enter fullscreen mode Exit fullscreen mode

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))) ); } } 
Enter fullscreen mode Exit fullscreen mode

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)