Tecnologia
Criando endpoints pensando em TDD
4 minutos de leitura
Fala, galera! Dessa vez eu decidi escrever uns códigos pensando no TDD. Isso mesmo! O tão famoso Test Driven Development! Bom, primeiro vou dar uma bela resumida e, quando escrevo resumida, é resumida mesmo, sobre o que é o TDD.
Basicamente é o que o nome diz, o desenvolvimento de softwares guiado por Testes. Vale dizer que isso começou com o Kent Beck, que foi o criador do TDD.
Fiz esse desenho para ilustrar melhor sobre:
E a ideia é exatamente essa mesmo, você escrever primeiro o teste unitário no seu código fonte, antes mesmo de escrever a funcionalidade em si. Ou seja:
1- Escreva um teste que vai falhar (exatamente porque a funcionalidade ainda nem existe).
2- Escreva a funcionalidade em si, com o código que vai fazê-la funcionar
3- Refatore o seu código, eliminando redundâncias, deixando ele mais elegante e etc…
Hands on
Chega de resumir e vamos colocar a mão na massa. Bom, vou criar um endpoint simples com Spring que o único objetivo é retornar uma lista e outro método em uma service que o objetivo é saber se uma idade que será passada como parâmetro é maior de idade ou não.
Vamos criar o projeto assim:
Vamos ao nosso primeiro endpoint, lembrando que primeiro vamos criar uma classe chamada Usuário, ela ainda não faz parte da nossa funcionalidade em si, mas será uma classe importante:
import lombok.Builder;
import lombok.Data;
@Data
@Builder
public class Usuario {
private String nome;
private String email;
private int idade;
}
Vamos adicionar ao nosso projeto essas duas dependências:
<!--junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<!--mockito-->
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
</dependency>
Feito isso, vamos criar a nossa UsuarioController da seguinte forma:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("usuario")
public class UsuarioController {
}
Repare que ela está vazia, sem implementação alguma e de fato essa é a ideia!
Agora vamos criar o teste da nossa controller:
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
@WebMvcTest(UsuarioController.class)
public class UsuarioControllerTests {
@Autowired
private MockMvc mockMvc;
@Test
public void testarListaUsuario() throws Exception {
this.mockMvc.perform(MockMvcRequestBuilders.get("/usuario")) //teste para acessar o endpoint
.andDo(MockMvcResultHandlers.print()) // print do resultado do teste
.andExpect(MockMvcResultMatchers.jsonPath("$").isArray()); // verificando se o endpoint retorna um array
}
}
Repare que nós já escrevemos o teste do que o nosso endpoint tem que fazer (retornar uma lista), porém ainda nem criamos a funcionalidade em si, por isso quando rodamos o teste, vai dar erro mesmo:
Perfeito! Agora que já sabemos o que o nosso endpoint tem que fazer e inclusive testamos isso, vamos criar de fato a funcionalidade:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("usuario")
public class UsuarioController {
@GetMapping
public List<Usuario> listaUsuario() {
List<Usuario> listaUsuarios = new ArrayList<>();
listaUsuarios.add(new Usuario("Nataniel", "[email protected]", 29));
return listaUsuarios;
}
}
Esse é o passo 2, como a funcionalidade está criada, agora podemos rodar de fato o teste trazendo um resultado correto:
Agora, vamos ao passo 3, que é o de refatorar o código do nosso endpoint:
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@RestController
@RequestMapping("usuario")
public class UsuarioController {
@GetMapping
public List<Usuario> listaUsuario() {
return Arrays.asList(
Usuario
.builder()
.nome("Nataniel")
.email("[email protected]")
.idade(29).build());
}
}
Agora no passo 3 podemos deixar o nosso código mais elegante! E a ideia do TDD é exatamente essa, você vai construindo as suas funcionalidades guiado pelos testes.
Agora vamos para a nossa segunda funcionalidade. Vamos precisar de uma Service com um método para validar se uma idade passada como parâmetro é maior de idade ou não.
Criamos apenas a “casca” da nossa service:
import org.springframework.stereotype.Service;
@Service
public class UsuarioService {
public boolean verificaMaiorIdade(int idade){
return true;
}
}
Até criei o método, mas repare que ele apenas tem o retorno para não ter erro de compilação. Agora vamos para o nosso teste:
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InjectMocks;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class UsuarioServiceTests {
@InjectMocks
public UsuarioService usuarioService;
@Test
public void verificaMaiorIdade() {
Boolean teste = usuarioService.verificaMaiorIdade(18);
Assert.assertTrue(teste);
}
@Test
public void verificaMenorIdade() {
boolean teste = usuarioService.verificaMaiorIdade(17);
Assert.assertFalse(teste);
}
}
Repare que um dos testes escritos vai trazer um falso positivo, porém estamos testando o que queremos de fato na funcionalidade. Então vamos criar a funcionalidade de fato.
import org.springframework.stereotype.Service;
@Service
public class UsuarioService {
public boolean verificaMaiorIdade(int idade){
boolean retorno = false;
if(idade >= 18){
retorno = true;
}
return retorno;
}
}
Repare que agora os dois testes que fizemos irão passar. Agora vamos tentar melhorar o nosso código:
import org.springframework.stereotype.Service;
@Service
public class UsuarioService {
public boolean verificaMaiorIdade(int idade){
return idade >= 18;
}
}
Feito! Após o código melhorado, basta rodar os testes e sucesso!!! Se quiseres o código completo, está nesse link do Github.
Mesmo assim, ainda ficou na dúvida? Veja esse vídeo:
Espero ter ajudado alguém! Valeu, galera! E até o próximo artigo!