TDD – Primeiros passos

O que é TDD? Por onde começar um Teste? Como se faz um Teste?

TDD é uma metodologia de validação das classes/métodos/atributos que serão criados. Por exemplo, foi solicitado criar um método que busque os dados de uma casa. Com o TDD, é possível validar todas as situações que o desenvolvedor possa imaginar, como: casa sem número, valor retornado null, dono se mudou, etc.

Por onde começar? Vamos configurar o JUnit para ficar mais clara nossa visão? Desculpe se você já sabe fazer isso, é coisa rápida (temos que ajudar quem está começando!).
Irei usar o Eclipse como IDE, mas na verdade o TDD pode (e deve) ser aplicado em qualquer IDE. Caso você use outra ferramenta de teste que não seja o JUnit, o conceito que será passado aqui é aplicável a qualquer ferramenta de teste.

OBS.: Esses pequenos passos de como criar classes/pacotes ou abrir uma view do Eclipse você pode achar em outro post aqui no blog: Criando um WebServer

Após criar um novo “Java Project” que chamarei de TDD, utilizando a View “Package Explorer”, adicione a biblioteca do JUnit:
Configurando 01
Configurando 02
Configurando 03

Vamos trabalhar em cima do seguinte caso de uso: Seu sistema terá uma classe User que manterá as informações de cada pessoa que faz o login no sistema. E como requisito, essa classe terá um método para bloquear o acesso de um usuário ao sistema.
Vamos criar um pacote chamado “test.com” e uma classe chamada TestUser dentro deste pacote.

package test.com;

public class TestUser {
}

A ideologia base do TDD é começar o teste pelo resultado esperado. (HEIN?!) No mercado o costume sempre foi começar a codificar pelo começo, mas no TDD nós começamos pelo fim. Por quê? Fazendo assim temos uma idéia para onde vamos, o que vamos precisar para chegar até lá, evitamos criação de parâmetros desnecessários, os métodos ficam mais objetivos e assim vai.

Ao escrever métodos temos que ter em mente a seguinte seqüência: “Vermelho, Verde, Refatorar”. O que seria isso? Quando escrevemos testes utilizando JUnit temos a barra (que em breve veremos) mostrando status vermelho para quando o teste falha, e verde para quando tudo é executado com sucesso.

Para garantir um resultado experado, existem métodos que fazem essa conferência. Vamos utilizar o assertEquals(valor desejado, valor retornado). Em nosso caso de uso, o que queremos? Sempre que o usuário estiver bloqueado o método terá que retornar verdade. Vamos escrever esse código?

package test.com;
import static org.junit.Assert.*;
import org.junit.Test;

public class TestUser {
    @Test
    public void isUserWithDeniedAccess(){
        assertEquals(true, user.accessDenied);
    }
}

Alguns detalhes sobre o código. Note que temos um annotation “@Test” e ao executarmos o framework do JUnit ele irá localizar os métodos que tiverem esse annotation, e irá executar a validação do teste. Estamos utilizando o método assertEquals que é importado do próprio JUnit utilizando o “import static”.

Perceba também que já estou “tentando” acessar um objeto que não existe tanto no método, tão pouco no projeto. Está lembrado de quando falei que começamos do fim? O nosso fim (alvo, meta) é que o usuário no final das contas esteja realmente bloqueado. Mesmo que não exista classe ou seus atributos ainda, iremos normalmente, como passo inicial, utilizá-lo em nosso código. Fazendo assim, já estamos pensando no nome da classe, dos atributos. Mesmo que não compile, estamos no caminho. Pequenos passos… Sempre pequenos passos…
Finalizando nosso teste, nosso código ficará assim:

@Test
public void isUserWithDeniedAccess(){
        User user = new User();
        assertEquals(true, user.accessDenied);
}

Note que foi “instanciada” a classe User mesmo sem ela existir no projeto. Agora temos um projeto em que o teste não é compilável.
Lembram-se dos passos do TDD? Vermelho, Verde e Refatorar? Temos que conseguir uma barra vermelha retirando os erros de compilação. Vamos criar nossa classe User (em um pacote chamado “com”) e adicionar o atributo necessário.

package com;
public class User {
    public boolean accessDenied;
}

Agora basta fazer o import na classe de teste (TestUser):

import com.User;

E já estamos prontos para conseguir nossa barra vermelha. Para rodar o teste pelo JUnit basta clicar com botão direito em cima da nossa classe de teste ir em “Run as > JUnit Test”.
Executando o JUnit
A barra vermelha nos indica que agora estamos prontos para trabalhar o nosso método para que ele possa retornar o valor desejado. Começamos pelo erro para saber que, ao conseguir o verde, nosso teste realmente esta trabalhando como desejamos. Nesse caso estamos a fazer um exemplo bem direto, mas se estivéssemos fazendo um método complexo, começar pelo erro faria com que tivéssemos a certeza de que atingimos nosso objetivo. Com isso, não precisamos executar toda a aplicação para fazer o teste da funcionalidade, basta rodar os testes e pronto, já podemos ver rapidamente o resultado da nossa alteração.

Agora vamos desenvolver nosso código para que nossa barra possa ficar verde? Nessa hora, geralmente, alteramos o método que acabamos de criar para retornar o valor desejado. Como nosso exemplo é simples, basta trabalhar com o valor do atributo para consigamos o valor desejado. O nosso teste é quem tem quem deve estimular a classe nas situações desejadas, por isso não estamos alterando em nada a classe User para que nosso teste execute. TDD trabalha com pequenos passos, caso você já esteja com um bom nível em TDD já seria possível criar os métodos de uma só vez. Por isso estamos acessando diretamente os atributos, mas de acordo com algumas literaturas de TDD é uma boa prática acessar os atributos pois você já estaria validando os mesmos em sua fase de teste, e depois, com a refatoração seriam criados os métodos para proteger os atributos

@Test
public void isUserWithDeniedAccess(){
        User user = new User();
        user.accessDenied = true;
        assertEquals(true, user.accessDenied);
}

E agora basta executar o JUnit o código que teremos a barra verde.

Note que nosso código está feio, e não está aplicando nenhum padrão OO. O que fazer? Esse ato de refatorar sempre fica para o final, então vamos adicionar ao final da nossa lista de tarefas onde iremos refatorar. Assim não iremos deixar nada para trás.

Usando essa abordagem de código temos uma visão melhor do código, de onde poderíamos alterá-lo ou melhorá-lo. E melhor, temos a certeza de que nosso código já está fazendo o que nós queríamos. Mas precisamos aplicar conceitos OO antes de dar a tarefa como concluída. Vamos primeiro criar um método para verificar se o usuário tem seu acesso negado ao invés de acessar o atributo direto.

// TestUser.java
package test.com;

import static org.junit.Assert.assertEquals;
import org.junit.Test;
import com.User;

public class TestUser {
    @Test
    public void isUserReallyBlocked(){
        User user = new User();
        user.accessDenied = true;
        assertEquals(true, user.hasAccesDenied());
    }
}

//User.java
package com;

public class User {
    public boolean accessDenied;

    public boolean hasAccesDenied() {
        return accessDenied;
    }
}

Não alteramos o acesso ao atributo ainda pois temos mais um passo pela frente. Vamos criar um meio para que a classe altere o atributo e proteja o atributo de um acesso direto. Para isso, vamos usar o conceito Diga, não pergunte. Fazendo assim, de um modo eficiente iremos terminar nosso caso de teste e nossa tarefa. Nosso código final seria:

package test.com;

import static org.junit.Assert.assertEquals;
import org.junit.Test;
import com.User;

public class TestUser {
    @Test
    public void isUserReallyBlocked(){
        User user = new User();
        user.denyAcces();
        assertEquals(true, user.hasAccesDenied());
    }
}

package com;

public class User {
    private boolean accessDenied;

    public boolean hasAccesDenied() {
        return accessDenied;
    }

    public void denyAcces() {
        accessDenied = true;
    }
}

Note como o código da classe User ficou claro, objetivo, sem métodos desnecessários. Começando pelo teste nosso foco é maior no comportamento. Basta agora compilar o teste utilizando o JUnit e ver que concluímos tudo com sucesso.

Pessoal, esperem que ajude esse post para os primeiros passos. Em breve irei colocar aqui, boas práticas de TDD para que possamos aprimorar ainda mais o nosso desenvolvimento. O post de hoje foi apenas uma introdução mais a ferramenta em si, no próximo post sobre TDD iremos falar mais sobre conceitos e abordagens do TDD.

Boa noite a todos.

4 thoughts on “TDD – Primeiros passos

  1. Ola de novo, como geek estou lendo todos seus posts, e realmente são muito bons, a uns tempos ando estudando TDD e mesmo tendo um conhecimento sobre o mesmo, post basicos são bem vindos.
    Acredique o a maior dificuldade do desenvolvedor utilizar TDD seja cultural, pensar no teste antes de começar a desenvolver um método é um pouco estranho a primeira vista. Voce me recomendaria algum livro sobre TDD?

    Parabens pelo post e até a proxima, abraços

    • Olá Luiz, boa tarde.

      Fico feliz por saber que meus posts estão podendo te ajudar em algo.

      Eu até hoje li um livro e muitos artigos. O livro que eu li, daria nota 7. Ele mostra conceitos fortes e bons de se ler, mas no meio do livro ele muda de assunto totalmente. Fica chato, o livro foi esse aqui: TDD – Kent Beck.

      Eu dei de presente para um amigo o livro JUnit in Action. Ele está lendo e falou que é muito bom.

      Que ele esse livro chega a abordar o testes em EJBs. Seria uma boa ler esse livro, está na minha lista de livros para ler.

      Até a próxima.

Leave a Comment