Blog > Tecnologia > Criando endpoints pensando em TDD

Criando endpoints pensando em TDD

17/12/2020
  • 4 min. 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", "nataniel.paiva@gmail.com", 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("nataniel.paiva@gmail.com")
                    .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!

    Gostou? 20
    Compartilhe esse post: