Coding Dojo Floripa

Desenvolvimento Ágil

Archive for Novembro, 2006

2ª Reunião do Dojo

Posted by Ivan Sanchez em Quarta-feira, Novembro 29, 2006

Nossa próxima reunião já tem local, data e hora marcada:

Data: 18 de Dezembro de 2006
Horário: das 19:00 às 22:00
Local: Auditório do Celta (Rodovia SC 401 Km 01 – Parque Tecnológico Alfa – João Paulo – Florianópolis – SC)

O desafio ainda não foi definido, mas será divulgado aqui com antecedência. A linguagem adotada nesta reunião será Java, mas não haverá exigência de conhecimento sobre ela para participar.

A reunião será aberta e gratuita, porém peço aos interessado que me enviem e-mail ou deixe comentário para tirar possíveis dúvidas e confirmar a presença.

Até lá 🙂

Update: o desafio já foi definido! Mais informações sobre ele aqui.

Anúncios

Posted in Dojo | 1 Comment »

Confusões sobre TDD

Posted by Ivan Sanchez em Terça-feira, Novembro 28, 2006

Lendo o post sobre Desenvolvimento Orientado a Testes no blog do José Oliveira notei que ele pode gerar algumas confusões comuns para que está começando a aprender sobre esta técnica. E digo isso por experiência própria, já que levei meses até começar a desenvolver orientado a testes com alguma desenvoltura.

O texto apresenta a seguinte definição de TDD:

“…TDD é simplesmente você escrever o teste do seu programa antes de codificá-lo de fato. Teste nesse contexto seriam os testes unitários.”

Ao meu ver a essência está correta, porém talvez faltasse deixar claro que TDD não se trata apenas de escrever testes antes do código. Esta é a primeira visão que a maioria dos iniciantes têm sobre desenvolvimento orientado a testes: que basta escrever todos os testes antes e pronto. Isto é compreensível, já que escrever testes antes do código já causa um choque por si só. Porém, TDD foi definido pelo Kent Beck com a seguinte fórmula:

TDD = Test-First + Design Incremental

E a parte do design incremental ficou um pouco confusa na continuação do texto:

“…O processo de desenvolvimento usando TDD é muito simples:

  1. Escolha a classe e o método que você que codificar e pense em todos os testes possíveis para ela;
  2. Antes de escrever a classe, codifique os testes que você pensou….”

Seguindo estas instruções (escrever todos os testes possíveis antes) pode-se perder um pouco de um dos maiores benefícios de TDD, que é prover feedback rápido sobre o que está sendo implementado. Aí que entra o design incremental. A idéia é que a solução seja criada em pequenos passos (Baby Steps), seguindo a rotina descrita no livro TDD by Example:

  1. Escreva um pequeno teste que falhe, e talvez até mesmo sequer compile inicialmente;
  2. Faça o teste passar da maneira rápida, cometendo qualquer “pecado” durante este processo;
  3. Refatore: elimine toda duplicação criada para fazer os testes passarem.

Desta forma, não é preciso pensar na solução completa (sequer numa classe inteira) antes de começar, basta pensar em um teste apenas e fazê-lo passar. Com o conhecimento adquirido é possível então passar para o próximo teste com mais segurança, e assim avançar até que a solução esteja completa.

Outra noção comum é pensar que TDD trata apenas de testes de unidade. Como bem disse o José no seu post, TDD faz bastante uso de testes unitários. Porém, embora a maioria dos exemplos (inclusive o meu) mostra TDD sendo feito através deste tipo de teste, isto não significa que a técnica está restrita a eles. Escrever testes de aceitação simples antes do código existir também pode colaborar na hora de desenvolver com TDD, já que com eles também pode-se obter feedback sobre a solução.

Embora sejam bem pontuais, observar estes detalhes pode ajudar no uso mais eficiente de TDD. E no geral achei o post do José Olivera bastante útil pra quem está começando 🙂

Apropósito, fiquei contente em ver que mais gente se interessa em escrever sobre TDD. É sinal que a técnica esta se tornando popular. Além disso, recomendo visitas regulares ao blog do José Oliveira. (quem sabe um dia eu ainda respondo o questionário que ele publicou por lá…)

Posted in TDD | 8 Comments »

Pausa para os comerciais…

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

Depois de quase duas semanas afastado, fiquei contente em voltar e me ver citado neste post do blog do Dudu Tomaselli.

O post faz parte de uma iniciativa da blogosfera para divulgação/recomendação de blogs. Aliás, pretendo fazer o mesmo somente com blogs de desenvolvimento de software, então se você possui um, deixe um comentário que terei o maior prazer em fazer uma visita!

Agora, voltemos com nossa programação normal…

Obs.: o afastamento foi por conta do meu casamento, então não deve ser algo que vá se repetir 😛

Posted in Geral | 2 Comments »

O que faz de você um bom programador

Posted by Ivan Sanchez em Quarta-feira, Novembro 15, 2006

Depois de vários anos trabalhando com desenvolvimento de sofware e conhecendo vários programadores diferentes, posso afirmar que existem algumas características “não-técnicas” que só os melhores possuem, dentre elas:

  • Objetividade:
    • Sabe onde quer chegar quando começa a implementar algo novo;
    • Diferencia bem a hora de aprender da hora de usar o que aprendeu;
    • Busca a solução mais simples e que funcione.
  • Comunicação:
    • Sabe “vender” uma idéia;
    • É capaz de expor suas idéias para um leigo;
    • Consegue distinguir o vocabulário técnico do de negócios.
  • Aprendizado:
    • Possui bastante background teórico;
    • Autodidata para ferramentas e linguagens;
    • Participa ativamente de várias listas de discussão;
    • Gosta de ler livros não-técnicos;
    • Busca participar de palestras, workshops e cursos.
  • Social:
    • Pratica algum esporte;
    • Consegue manter um ótimo relacionamento com colegas e clientes.

Acredito que conhecimento técnico é importante, mas sem alguns destes itens este conhecimento não serve para muito. Hoje em dia tenho a sorte de trabalhar com pessoas que reunem a maioria destas características.

E você, acrescentaria ou tiraria algum item desta lista?

Posted in Dojo, Programming | 18 Comments »

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 🙂

Posted in TDD | 7 Comments »

Exemplo TDD, parte 1: Por onde começar

Posted by Ivan Sanchez em Quinta-feira, Novembro 9, 2006

Aviso! Todo o contexto deste exemplo é 100% fictício.
Qualquer um pode colaborar com sugestões/críticas/perguntas, então participe!

Começamos há pouco tempo um novo projeto de um sistema para controlar o acesso e acompanhar a frequencia dos estudantes de um cursinho pré-vestibular. A integração com a catraca ainda não é prioridade, então a nossa primeira tarefa é: armazenar e contabilizar as horas dos estudantes.

Nossa intenção é desenvolver orientado a testes, então surge a pergunta: por onde começar?

Como já sabemos, o primeiro passo para criação de código usando TDD é criar um teste que falhe. Embora o primeiro teste pareça sempre o mais complicado para se escrever, depois de algum treino ele acaba se tornando tão simples quanto os demais. Isso porque não há mesmo muita diferença entre eles.

Então como escrever o primeiro teste? A resposta é: não importa, desde que ele seja escrito. Não há necessidade de se preocupar se o teste está errado, uma vez que o código da aplicação sequer existe. Se ele parecer errado basta reescrevê-lo, e isso poderá ser feito em qualquer momento, mesmo depois de existirem centenas de testes.

Um teste é a descrição de um comportamento do sistema. Então basta termos uma idéia qualquer do que o sistema tem que fazer que já podemos começar.

Resolvemos começar escrevendo uma classe para representar as horas dos estudantes. Começamos com um item qualquer na nossa checklist mental:

  • Representar uma hora qualquer no formato hh:mm

Então vamos fazer o nosso primeiro teste para criar um objeto da classe Hora com um determinado valor e verificar se este foi armazendo. O código fica assim:

package dojo.exemplo.tdd;
import junit.framework.TestCase;
public class HoraTest extends TestCase
{
    public void testCriacaoHoraQuinzeHoras()
    {
        Hora hora = new Hora("15:00");
        assertEquals("15:00", hora.toString());
    }
}

Estamos especificando um comportamento esperado da nossa classe e fazemos uma validação, o que significa que nosso primeiro teste está pronto. Porém este ainda não compila porque não temos a classe Hora e seu construtor. Então chegou a hora de criá-la:

package dojo.exemplo.tdd;
public class Hora
{
    public Hora(String string)
    {
    }
}

Isso é o mínimo que precisamos para rodar o primeiro teste. E com isso temos o nosso primeiro erro:

junit.framework.ComparisonFailure: expected: but was:

Nossa tarefa agora é fazer o teste passar, e a maneira mais simples para isso é criar o método toString() na classe Hora, retornando uma constante:

    public String toString()
    {
        return "15:00";
    }

Com esta alteração o teste agora passa. Porém sentimos que ainda podemos melhorar, e para isso só temos duas alternativas válidas: fazer um refactoring ou escrever um novo teste.

Decidimos fazer um novo teste na classe HoraTest para quebrar esta primeira implementação:

    public void testCriacaoHoraDezEMeia()
    {
        Hora hora = new Hora("10:30");
        assertEquals("10:30", hora.toString());
    }

Isto já é o suficiente para fazer o teste falhar, o que já esperávamos uma vez que nossa classe não permite criar uma hora diferente de 15:00. Portanto precisamos modificá-la:

    private String hora;
    public Hora(String string)
    {
        this.hora = string;
    }
    public String toString()
    {
        return this.hora;
    }

Rodamos os nossos dois testes e agora ambos passam. E agora já é possível pensar em pelo menos mais duas classes que a classe tem que fazer:

  • 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

(Começaremos a próxima parte do nosso exemplo resolvendo estes dois itens)

Posted in TDD | 10 Comments »

Introdução ao Desenvolvimento Orientado a Testes (TDD)

Posted by Ivan Sanchez em Terça-feira, Novembro 7, 2006

Test-Driven Development (TDD) ou Desenvolvimento Orientado a Testes é uma maneira diferente de escrever software. A diferença entre esta abordagem e a que você provavelmente está acostumado é que com TDD você vai evoluindo seu código aos poucos, conforme vai explorando o problema com o uso de testes automatizados escritos antes da solução sequer existir.

Algumas vantagens desta abordagem são:

  • Incentiva a simplicidade: como a solução vai surgindo pouco a pouco, a tendência é que não se perca tempo com aquilo que não tem certeza que será usado em seguida. Expressões como “You Ain’t Gonna Need It (YAGNI)” e “Keep It Simple, Stupid (KISS)” são recorrentes quando se está programando orientado a testes.
  • Aumenta a confiança no código: o sistema funciona de uma determinada maneira porque existem testes que foram utilizados durante sua criação e validam o que foi criado. E se ainda assim algum erro surgir, um novo teste é criado para reproduzí-lo e garantir que depois de solucionado ele não irá se repetir.
  • Ajuda como documentação: os testes quando bem definidos são mais simples de ler que o código e, embora nem sempre sirvam como uma especificação para o usuário final, eles são uma fonte eficiente para entender o que o software faz. Além disso, esta documentação sempre estará atualizada com a aplicação.
  • Facilita refactorings: quanto mais testes existem no sistema, maior é a segurança para fazer refactorings. Um erro causado por algum refactoring dificilmente vai passar desapercebido quando um ou mais testes falharem após a mudança.

Em TDD, um teste é um pedaço de software. A diferença entre teste e o código que está sendo produzido é que os testes têm 2 funções principais:

  • De especificação, ou seja, definir uma regra que seu software deve obedecer
  • De validação, ou seja, verificar que a regra é obedecida pelo software

Geralmente os testes são criados com algum framework do tipo xUnit (jUnit, nUnit Test::Unit etc) , mas também podem ser feitos num nível de funcionalidades (através de softwares como o FitNesse e Selenium) . Estas ferramentas servem basicamente para organizar os testes e facilitar na criação das verificações.

O processo de criação de programação orientado a testes é simples:

  1. Escreva um teste que falhe. Pense no que o código deve fazer, descreva o contexto e defina quais são as verificações que precisam ser feitas. Não há um limite no número de testes, então quanto menos coisa cada teste descrever/verificar, melhor. No início também não é preciso se preocupar se a classe/método ainda não existe. Pense primeiro no teste e só depois que este estiver pronto crie o esqueleto de código necessário para que ele compile e falhe ao rodar.
  2. Faça o teste passar. Agora chegou o ponto crucial: escreva o mínimo de código para que o teste passe. Controle o instinto natural do programador de tentar prever tudo que o código vai fazer e apenas faça o teste passar. Mesmo que tenha certeza que o código deve fazer mais coisas, fazer os testes passarem deve ser a única preocupação agora.
  3. Refatore. Uma vez que o teste passou, verifique o que no código pode ser melhorado. Geralmente para um teste passar é preciso inserir duplicação através de constantes (técnica conhecida como Fake It). Agora é a hora de melhorar o código e remover as duplicações, lembrando que os testes devem continuar passando.

Estes 3 passos são repetidos até que não se consiga pensar em novos testes, o que indica que a funcionalidade está pronta.

Posted in Geral, TDD | 28 Comments »

Top 5 desculpas de quem não quer programar orientado a testes

Posted by Ivan Sanchez em Sexta-feira, Novembro 3, 2006

Quando comecei a usar TDD regularmente, várias vezes tentei me enganar para de alguma forma poder ignorar os testes e partir direto para codificação. E inevitavelmente depois eu via que com os testes eu poderia ter facilitado a minha vida.

Depois de um tempo eu vi que outras pessoas também usavam as mesmas desculpas que as minhas. Então resolvi escrever comentando um pouco sobre as principais desculpas para não fazer TDD que eu conheço. São elas:

  1. Vai demorar muito mais: esta é uma das mais populares, utilizada inclusive pelos gerentes que se enganam pensando que ter qualquer coisa pronta é sinal de produtividade. Só que a maioria das pessoas adeptas desta desculpa pensam que o tempo de desenvolvimento de uma funcionalidade é só aquele utilizado para entregá-la. Supondo que uma pessoa produziu todo o código utilizando basicamente seus instintos e disse “terminei” sem nenhum tipo de validação, os riscos de ter alguma coisa incorreta aumentam bastante. E é neste ponto que TDD apresenta uma vantagem. O programador é obrigado a explorar melhor o problema, e ao terminar de codificar já terá uma série de testes automatizados para mostrar que aquilo que foi feito funciona. Ou seja, para entregar pode até ser que demore mais, mas para ter uma funcionalidade de acordo com os requisitos e sem defeitos o tempo provavelmente será menor.
  2. A funcionalidade é muito fácil: se depois de uma breve avaliação o sujeito achar que a solução a ser programada é muito fácil, ele está correndo três riscos: 1) Não entendeu todas as variações do problema; ou 2) Vai programar com displiscência; ou 3) Planeja usar e abusar de um dos piores inimigos da programação, o Ctrl+C, Ctrl+V. Em todos os casos a possibilidade de fazer besteira é iminente e poderia ser diminuída com testes, onde o problema seria explorado melhor antes do código e haveria a chance de remover a duplicação de código com auxílio de testes. Estes testes também confirmariam que a mudança não quebrou outra parte do sistema, o que indica que deveriam haver testes para todo o sistema.
  3. Não sei como testar: quem está começando a usar TDD tem essa desculpa na ponta da língua. É como se não conhecesse uma biblioteca e isto o impedisse de programar. Mas programar orientado a testes não é tão difícil assim, afinal se trata apenas de programação, coisa que já estamos acostumados. Basta pensar no problema que as perguntas surgem e aí estão os testes. Escreva a pergunta, pense na resposta e a escreva também. Com o tempo as perguntas vão ficando melhores, mas não saber fazê-las não pode ser a razão para não começar.
  4. Isso não dá pra testar: este é um mito que não tem fundamento. Tudo é testável, embora alguns casos sejam mais difíceis ou exijam um pouco mais de pesquisa. Dificilmente você terá um cenário que outros não tenham conseguidos testar, e neste caso é só aprender como se faz. Se ainda assim o cenário for inédito na literatura, existe uma comunidade grande sempre disposta a ajudar, então só basta querer. Ah! E na maioria dos casos usar mocks e stubs resolvem os problemas ditos como “não testáveis” pelos iniciantes.
  5. Melhor deixar os testes com testadores: aqui é um exemplo de que não se entendeu pra que serve TDD afinal. Escrever testes de unidade por si só não significa programar orientado a testes, uma vez que os testes são escritos antes do código existir e de maneira incremental. Então deixar os testes com os testadores significa abrir mão de uma ferramenta de modelagem simples, pensando que esta se trata apenas de testes.

Posted in Geral, TDD | 6 Comments »