Coding Dojo Floripa

Desenvolvimento Ágil

Exemplo TDD, parte 2: Baby Steps e Tratamento de Exceções

Posted by Ivan Sanchez em Segunda-feira, Novembro 13, 2006

Terminamos a primeira parte do nosso exemplo com os seguintes itens para implementar:

  • Representar uma hora qualquer no formato hh:mm
  • Permitir a criação de uma hora padrão (”00:00″)
  • Não permitir que se crie hora fora do formato hh:mm

Então começamos nosso segundo item com um teste simples:

    public void testCriacaoHoraPadrao(){
        Hora hora = new Hora();
        assertEquals("00:00", hora.toString());
    }

O teste não compila porque o construtor ainda não existe, então é preciso criá-lo, alterando a classe Hora:

    public Hora()    
    {
    }

Mais uma vez o teste falha:

junit.framework.ComparisonFailure: expected: but was:

Então temos que fazê-lo passar:

    public Hora()
    {
        this.hora = "00:00";
    }

Perceba que até agora todos os testes foram bem simples. Se você está se perguntando quando os testes vão ficar realmente complexos, provavelmente ainda não pegou o espírito de TDD. A idéia é que os testes continuem sempre o mais simples possível, e isso é feito usando “baby steps”.

Baby steps (passos de bebê) é um termo que expressa como o desenvolvimento incremental proposto no TDD deve ser feito. A idéia é implementar a aplicação pouco a pouco, para que a cada pequena mudança no software seja possível obter o feedback sobre aquilo que foi implementado.

Se você quiser se aprofundar no assunto, uma boa fonte é a página da ImproveIt. Voltando ao nosso exemplo, vamos para o nosso próximo item:

  • Representar uma hora qualquer no formato hh:mm
  • Permitir a criação de uma hora padrão (”00:00″)
  • Não permitir que se crie hora fora do formato hh:mm

Uma maneira de tratar este item é com o uso de exceções, então criamos o teste:

    public void testCriacaoHoraFormatoInvalido()
    {
        try
        {
            new Hora("00-00");
            fail("uma exceção deveria ter sido lançada");
        } catch (RuntimeException e)
        {
            assertEquals("Formato de Hora inválido: 00-00", e.getMessage());
        }
    }

Este teste especifica que se tentarmos criar uma hora como “00-00″, uma exceção “Formato de Hora inválido: 00-00″ deve ser lançada. Se isto não ocorrer o nosso teste vai falhar. Rodamos o teste pela primeira vez e ele falha, pois ainda não fazemos este controle:

junit.framework.AssertionFailedError: uma exceção deveria ter sido lançada

Agora temos que tratar isso no nosso código. Isto pode ser feito alterando o construtor da classe Hora:

    public Hora(String string)
    {
        if ("00-00".equals(string))
        {
            throw new RuntimeException("Formato de Hora inválido: 00-00");
        }
        this.hora = string;
    }

Todos os testes agoram passam, mas não estamos satisfeitos com o uso de constantes, então criamos mais um teste:

    public void testCriacaoHoraFormatoInvalidoStringQualquer()
    {
        try
        {
            new Hora("string qualquer");
            fail("uma exceção deveria ter sido lançada");
        } catch (RuntimeException e)
        {
            assertEquals("Formato de Hora inválido: string qualquer", e.getMessage());
        }
    }

Quando rodamos este teste a exceção não é lançada então temos mais uma vez o erro:

junit.framework.AssertionFailedError: uma exceção deveria ter sido lançada

Então é hora de pensarmos em como fazer este novo teste passar. Vamos tentar usando uma expressão regular para verificar o formato da hora. Alterando a classe Hora mais uma vez temos:

    public Hora(String string)
    {
        if (!string.matches("[0-9][0-9]:[0-9][0-9]"))
        {
            throw new RuntimeException("Formato de Hora inválido: 00-00");
        }
        this.hora = string;
    }

Rodamos mais uma vez o teste e eis que ele falha mais uma vez:

junit.framework.ComparisonFailure: expected:<...string qualquer> but was:<...00-00>

Estamos fazendo a validação da string, mas a exceção continua sendo lançada errada. Então vamos corrigí-la:

            throw new RuntimeException("Formato de Hora inválido: "+string);

Agora sim todos os testes passam, e novas questões aparecem: e se a hora for do tipo “2:15″? o sistema deve aceitar horas acima de “23:59″?

Analisando melhor o problema nos convencemos que “2:15″ é um formato válido. A outra questão é resolvida quando verificamos que precisaremos representar não apenas as horas do dia, mas uma quantidade de horas qualquer. Afinal, vamos contabilizar as horas dos estudantes. Isso também significa que vamos ter que somar horas (+) e não faz sentido fixar a hora entre 00:00 e 23:59. Então podemos atualizar nossa lista:

  • Representar hora no formato hh:mm
  • Permitir a criação de uma hora padrão (“00:00″)
  • Não permitir que se crie hora fora do formato hh:mm
  • Pode haver uma hora do tipo h:mm
  • Pode representar horas acima de 23:59, então uma hora como 300:00 é válida
  • Horas negativas são válidas
  • Deve ser possível fazer soma de horas

Já temos bastante trabalho para a próxima parte do exemplo. Então até a próxima :-)

About these ads

7 Respostas to “Exemplo TDD, parte 2: Baby Steps e Tratamento de Exceções”

  1. Leandro Zis said

    Ivan,

    Deixar o “parse” na classe Hora é me parece uma violação do principio que uma classe deve ter somente uma responsabilidade
    SRP . Eu acho melhor separar em outra classe.

    Leandro

  2. Se a responsabilidade da classe for apenas “Representar uma quantidade de Horas”, o parse pode continuar ali e ela continua coesa.

    Da mesma forma, se “Analisar o formato da Hora” for uma responsabilidade independente, pode-se retirar este controle e colocá-lo em outra classe sem problemas também.

    De qualquer maneira, tentei me guiar pelo princípio de “Fazer a coisa mais simples que funcione”. Neste exemplo de TDD, este princípio vem antes do SRP, que pode ser aplicado através de refactoring assim que julguemos necessário.

    Assim sendo, te pergunto: adicionar uma nova classe neste ponto do desenvolvimento deixaria a solução mais simples?

  3. Leandro Zis said

    A resposta é não.

    Eu só acho que o nome da classe Hora não expressa claramente suas responsabilidades que é de guardar a hora e analizar o formato da hora.

  4. [...] Ao final da segunda parte do nosso exemplo, terminamos com a seguinte lista de implementação: [...]

  5. [...] uma mencionada em TDD aqui no ProfissionaisTI. Mas para relembrar, utilizando a prática “baby steps” temos os seguintes [...]

  6. [...] deixe de lado a solução mais elegante temporariamente. Se possível siga a prática de baby steps. Procure ajuda em um fórum ou lista de discussão, e implemente uma solução mais simples e [...]

  7. [...] post anterior, introduzi o conceito de TDD. Mas para relembrar, utilizando a prática “baby steps” temos os seguintes [...]

Deixar uma resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

WordPress.com Logo

Está a comentar usando a sua conta WordPress.com Log Out / Modificar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Log Out / Modificar )

Facebook photo

Está a comentar usando a sua conta Facebook Log Out / Modificar )

Google+ photo

Está a comentar usando a sua conta Google+ Log Out / Modificar )

Connecting to %s

 
Seguir

Get every new post delivered to your Inbox.

%d bloggers like this: