Ir para o conteúdo

Criando endpoints pensando em TDD

4 min. de leitura

Avatar de Nataniel Freire de Paiva Amorim

Nataniel Freire de Paiva Amorim Autor


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:

https://www.youtube.com/watch?v=0VvmxALOPKw

Espero ter ajudado alguém! Valeu, galera! E até o próximo artigo!

Gostou?