Hoje eu passei por um artigo em inglês intitulado “Pare de Fazer Teste Unitário de Tudo”.

Minha idéia aqui não é falar sobre esse artigo mas apenas comentar duas sentenças que estão lá como pontos negativos do teste unitário.

Mesmo fora do seu contexto original, estas sentenças isoladas representam pensamentos bem comuns sobre unit testing.

A primeira sentença 

Sempre que nós precisamos alterar nosso código, custa tempo para alterar nossos testes unitários.

Se você usa TDD, notará que este raciocínio está invertido.

Um teste unitário descreve o que uma parte do sistema deve fazer, e valida se o sistema está mesmo fazendo aquilo.

Usando TDD, eu nunca altero um teste para acompanhar uma mudança no sistema mas sim o inverso: eu primeiro altero o teste para que ele passe a descrever o novo comportamento do sistema, e depois eu altero o sistema para que ele passe no teste!

Em TDD, portanto, o teste unitário não é um fardo para ser carregado pelo sistema, e talvez seja justamente o inverso! Imagino que no futuro escreveremos apenas os testes, e o computador vai se programar para atendê-los.

Mas pode ser mesmo um fardo escrever ou corrigir o teste depois de tudo pronto…

Eu me lembro que nos primórdios do meu trabalho com xUnit, eu escrevia meus testes unitários como sendo a cereja do bolo em uma implementação linda e já funcionando.

Pouco tempo depois, isso perdeu a graça e ser obrigado a escrever o teste depois de já ter terminado o serviço era mesmo muito chato – até porque era mais difícil pois o design não tinha ficado tão bacana quanto ele tem que ficar quando se quer escrever os testes primeiro.

E o próprio teste não ficava bacana; acabava testando o que não devia e deixava de testar o que deveria, pois ele ficava limitado ao que se pode fazer quando se escreve testes para um design não muito bom.

A outra sentença

Testes unitários só podem testar aquilo que nós pensamos que poderia ser um bug; não o que realmente impacta nossos usuários.

Mais uma vez, pra quem usa TDD isso soa bem esquisito.

TDD não tem nada a ver com escrever teste para prevenir um bug; e TDD tem tudo a ver justamente com escrever testes para aquilo que impacta nossos usuários.

Realmente, se eu deixar para escrever o teste depois, é possível que o meu raciocínio seja: “o que será que pode dar bug no futuro e que eu gostaria de prevenir com um teste?”. E é provável também que eu não seja muito bom em prever o futuro e escreva o teste errado.

Mas, se eu escrevo o teste antes, o raciocínio não tem nada a ver com isso. Usando TDD, meu racioncínio é: “que teste eu devo escrever para que, quando ele estiver passando, demonstre que a funcionalidade está pronta e faz o que deveria fazer?”.

Além disso, se eu usar TDD ao pé da letra, não vou adicionar nenhuma fração de funcionalidade sem ter antes escrito um teste para ela, não porque eu tenho medo de bugs mas sim porque eu quero ter feedback rápido de que que o que eu estou fazendo está funcionando, de que eu estou tendo progresso no desenvolvimento do sistema.

Ora, se estou escrevendo testes para funcionalidades e não para bugs, estou preocupado justamente com o que impacta o usuário em vez de praticar o exercício ingrato de tentar adivinhar o que pode dar errado. E pelo simples fato de todas as funcionalidades estarem cobertas, as chances de bug são naturalmente bem menores.

Tenho visto que cada bug encontrado provém de um teste que eu pulei (escrevi direto a funcionalidade, sem escrever o teste antes), ou então não é bug mas sim a ausência de uma funcionalidade que não foi prevista (do tipo: “hmmm, não sabia que o usuário precisava disso”) e isto é bastante aceitável.

Para encerrar

Agora, falando sobre o título deste post, será que o verdadeiro poder do teste unitário reside apenas na prática de TDD?

A maioria dos argumentos negativos que vejo sobre testes unitários me fazem balbuciar “ué, mas não é assim que eu faço teste unitário…”.

E estes argumentos geralmente tratam o teste como algo para ser feito depois de implementar a funcionalidade. Ou então até mencionam o TDD mas parecem ignorar como de fato se usa TDD.

Fim.

Campanha pelo uso moderado de if

Publicado: 5 de janeiro de 2016 em Uncategorized
Tags:

Os dois comandos que eu menos gosto nas linguagens de programação são o if e o switch-case, então eu procuro sempre usá-los com parcimônia (eita palavra bonita!).

Vou mostrar aqui uma opção simples para eles num cenário onde seu uso costuma ser considerado legítimo: factories.

O cenário

É comum o código ter menos if ao usar polimorfismo.

Neste cenário (os exemplos estão em Java), eu faço um parser em uma mensagem string a fim de obter a mensagem tipada em um objeto.

    public Mensagem parseMensagem(String tipoMensagem, String mensagem) {
         
         MensagemParser parser = ParserFactory.create(tipoMensagem);
         return parser.parse(mensagem);
     }

Então eu tenho parsers especializados em interpretar diferentes tipos de mensagens string.

Como eu sou muito esperto, eu fiz com que os parsers implementassem uma interface comum (MensagemParser), de modo que eu posso interpretar todos os tipos de mensagens com um único bloco de código.

Assim, graças ao polimorfimsmo do parser entregue pelo factory, não interessa quantos tipos de mensagem existam, eu trato todos da mesma maneira. Bacana!

Mas mesmo usando polimorfimsmo ainda caímos nos ifs na hora de implementar o factory:-/

Exemplo de factory usando if

O meu factory usando if fica assim:

    class ParserFactory {
        static MensagemParser create(String tipoMensagem) {
             
             if ("A".equals(tipoMensagem)) {
                 return new AParser();
             } else if ("B".equals(tipoMensagem)) {
                 return new BParser();
             } else if ("C".equals(tipoMensagem)) {
                 return new CParser();
             } else {
                 return new DParser();
             }
         }
     }

Usando case ficaria semelhante (semelhantemente ruim hahaha).

Exemplo de factory usando um dicionário em vez de if

Agora, vejamos como ficaria o meu parser usando um dicionário (ou “map”) em vez de if:

    class ParserFactory {
        private static Map<String, MensagemParser> parsers = new HashMap<>();
         static {
             parsers.put("A",      new AParser());
             parsers.put("B",      new BParser());
             parsers.put("C",      new CParser());
             parsers.put("D",      new DParser());
         }
         
         static MensagemParser create(String tipoMensagem) {
              return parsers.get(tipoMensagem);
         }
     }

Na minha opinião, este segundo exemplo é menos imperativo e mais declarativo; o código fica mais simples, mais legível. O que você acha?

Considerações

Este é um exemplo de factory bem básico, mas eu tenho usado o conceito com sucesso em casos mais complexos também.

Por exemplo, se precisar retornar uma nova instância do parser a cada requisição, basta guardar o tipo no dicionário em vez de guardar a própria instância.

Em Java também é possível usar enumerations para obter resultado semelhante, já que em Java um enum pode ter propriedades e métodos.

Além disso, o conceito também é válido para outras coisas além de factories. O dicionário poderia conter outros tipos de objetos (inclusive como índice, no lugar de string) e, em outras linguagens, poderia conter funções/ações/delegates.

É claro que se fosse necessário validar o tipo da mensagem talvez fosse preciso usar um if. Mas daí seria um único if em vez de muitos.

Fim.

 

agilemanifestobackground

Encontrei por aí algumas listas de “práticas ágeis”. A maioria destas listas confunde “técnicas” e “práticas”, que são coisas distintas; e mesmo a implementação de 100% de uma destas listas não é o bastante para se alcançar o desenvolvimento Ágil de software; ao contrário, isso poderia nos distanciar dele.

Acontece que o Ágil não é uma recomendação de práticas nem de técnicas – o Ágil é uma recomendação de atitude mental, é uma cultura. As práticas e técnicas estão disponíveis para podermos suportar esta cultura e recolher os benefícios dela.

Para ser Ágil é preciso primeiro assimilar o Manifesto Ágil, depois assimilar práticas que suportem o manifesto, depois implementar técnicas que viabilizem estas práticas, e depois recorrer novamente ao manifesto para saber se você está fazendo demais ou de menos e não perder de vista o grande objetivo.

Neste post eu percorro os 12 princípios do Manifesto Ágil, listando algumas práticas que ajudam a suportar cada um destes princípios. Eu procurei me limitar ao âmbito das práticas, ou seja: tentei não explicar o manifesto em si nem adentrar nas técnicas, pois só as práticas já são um assunto bem extenso. Você vai observar, mesmo quando eu não mencionar, que algumas práticas suportam mais de um princípio e que alguns princípios suportam outros.

Vamos lá, primeiro princípio:

1. Nossa maior prioridade é satisfazer o cliente através da entrega contínua e adiantada de software de valor.

Eu já disse em outro post que este é o princípio que define o Ágil em si e que os demais princípios apresentam a cultura proposta para se alcançar esse objetivo. Mas mesmo este princípio sendo tão abrangente, ainda posso destacar algumas práticas para suportá-lo:

  • Fatiar as demandas em requisitos de software, onde cada um destes requisitos seja pequeno, independente, quando pronto possa ser demonstrado para o cliente e que tenha valor para ele.
  • Deixar o cliente decidir o valor de cada requisito:

Um cliente inexperiente no desenvolvimento Ágil vai decidir que apenas a implementação de 100% dos requisitos tem valor para ele. Neste caso é preciso ensiná-lo a identificar o valor de cada pequena parte, e ensiná-lo a se beneficiar de entregas incrementais. Minha experiência diz que os clientes aprendem isso com mais facilidade que os desenvolvedores. Anime-se! E constate que seu cliente aprenderá a trabalhar assim muito mais rápido do que você aprendeu.

  • Priorizar a implementação dos requisitos de acordo com o valor para o cliente:

Se os requisitos são independentes, então temos condições de implementar primeiro os que são mais importantes para o cliente neste momento. Devemos começar por estes.

Estas práticas aliadas ajudam a suportar o primeiro princípio, já que requisitos pequenos podem ser feitos em menos tempo, permitindo a entrega adiantada, e já que os primeiros requisitos implementados foram justamente os de maior valor para o cliente.

A parte “entrega contínua” vou abordar daqui a pouco em outro princípio.

2. Mudanças nos requisitos são bem-vindas, mesmo tardiamente no desenvolvimento. Processos ágeis vêem nas  mudanças oportunidade de vantagem competitiva para o cliente.

Na medida que entregamos software para o cliente, ele muda de ideia sobre o que vai querer a seguir, e pode querer mudar mesmo aquilo que já foi feito. Isso é natural pois ele aprende mais sobre suas necessidades quando vê o software funcionando. Além disso, suas necessidades podem de fato mudar devido a variações no seu ambiente.

Para suportar estas mudanças de requisitos a custo mais baixo, podemos:

  • Investir no detalhamento apenas dos requisitos que estão prestes a ser implementados, evitando o desperdício de detalhar agora os requisitos que podem nunca vir a ser implementados.
  • Obedecer estritamente a ordem de prioridades definida pelo cliente:
Não desenvolva agora os recursos que possuem menor valor neste momento. Não caia na tentação de desenvolver agora um requisito que não esteja fechado ou que não seja prioridade só para preencher o tempo disponível. É melhor investir esse tempo melhorando a qualidade daquilo que já foi feito e que tem valor já bem determinado.
  • Garantir que as decisões a cargo do cliente são de responsabilidade de uma (e somente uma) pessoa:

Esta pessoa é do lado do cliente, ela é mais de negócio que de TI, está engajada no desenvolvimento do software, e tem capacidade e autonomia para tomar estas decisões. Ela conta com a ajuda de muita gente, mas é esta uma pessoa quem tem o poder.

3. Entregar frequentemente software funcionando, de duas semanas a dois meses, com preferência à menor escala de tempo.

O primeiro princípio já declara a importância de entregar rápido a parte do software que tem mais valor neste momento, e diz que devemos fazer isso continuamente. Este terceiro princípio dá o intervalo de tempo em que devemos fazer isso. Para entregar mais software de valor e funcionando a cada dua semanas, podemos:

  • Automatizar os testes pois não dá tempo de desenvolver e ainda testar tudo manualmente em tão pouco tempo.
  • Automatizar o ambiente, pois não dá tempo de fazer tudo manualmente:

Todas as tarefas repetitivas devem ser automatizadas: integração de componentes alterados, integração das alterações de banco de dados, compilação, geração de pacote de distribuição, atualização do ambiente de testes, atualização do ambiente de homologação, atualização do ambiente de produção…

  • Quebrar os requisitos que custem mais que alguns dias para serem implementados.

4. Pessoas de negócio e desenvolvedores devem trabalhar diariamente em conjunto por todo o projeto.

  • Evoluir os requisitos (junto com o cliente, óbvio) ao longo de todo o projeto, sem iludir-se de que se pode definir todos os requisitos no começo e voltar com o sistema pronto ao final.
  • Manter as pessoas de negócio acessíveis aos desenvolvedores para que possam tirar suas dúvidas.
  • Manter os desenvolvedores acessíveis às pessoas de negócio, para que a comunicação direta surta seus efeitos benéficos e para permitir que os desenvolvedores obtenham feedback real do seu trabalho (feedback positivo e negativo).

5. Construa projetos em torno de indivíduos motivados. Dê a eles o ambiente e o suporte necessário e confie neles para fazer o trabalho.

Este princípio é o que exige as práticas mais difíceis, pois não depende apenas do time e do cliente. Mas é um princípio indispensável. Nem considere a possibilidade de ter um desenvolvimento Ágil de software sem suportar este princípio. Eis algumas práticas:

  • Escolha para o seu time apenas as pessoas que estão interessadas no trabalho.
  • Forneça café, estação de trabalho de alta performance, cadeira confortável e ambiente descontraído, ou deixe que o cidadão trabalhe em casa no ambiente que ele preferir.
  • Ofereça oportunidades para o desenvolvimento de cada pessoa no time.
  • Ajude a resolver os problemas que atrapalhem a produtividade do time.
  • Estimule a criatividade e a inovação.
  • Espere os resultados.

6. O método mais eficiente e eficaz de transmitir informações para e dentro de uma equipe de desenvolvimento é a conversa face a face.

Escrever é preciso; escrever tudo é desperdício. Portanto:

  • Mantenha sucintos os requisitos escritos e invista na conversa para aprofundar seu entendimento:

Requisito ou descrição de solução com dezenas de páginas e diagramas de casos de uso são ineficientes, ceifam a criatividade, resultam em soluções incompletas. Conversa face a face é mais barato, rápido e eficaz.

  • Prefira o telefone ao e-mail.
  • Prefira conversa pessoal mais que telefone.
  • Estimule o diálogo entre os desenvolvedores.
  • Mantenha times pequenos pois a comunicação face a face eficaz é impossível em um time com dezenas de pessoas.

7. Software funcionando é a medida primária de progresso.

Gráficos burndown, burnup, cumulative flow, lead time e outras métricas e monitoramentos são importantes para a gestão do projeto, mas são inúteis se não houver software real sendo entregue continuamente. Este princípio é suportado pelo terceiro princípio, mas seguem abaixo algumas práticas adicionais:

  • Rastreie a quantidade de requisitos ou quantidade de valor já entregue ao cliente em vez de rastrear quantidade de pontos concluídos:

Pontos por função, ou story points ou pontos de complexidade, ou qualquer outra coisa você use para estimar o custo de um requisito são para uso interno do time e não servem para os demais stakeholders acompanharem a evolução do projeto.

  • Defina junto com o cliente critérios de aceite para cada requisito, ou seja, como o cliente vai analisar o software para aceitar o requisito como concluído.
  • Crie a definição de pronto interna do time:

Exemplo: (para cada requisito) critérios de aceite observados, testes automatizados implementados, código enviado para o repositório, testes executados com sucesso. Adote esta prática apenas enquanto ela ainda agregar valor para o time (por exemplo, enquanto você ainda tiver pessoas no time que costumam declarar “está pronto; agora só falta testar”).

8. Os processos ágeis promovem desenvolvimento sustentável. Os patrocinadores, desenvolvedores e usuários devem ser capazes de manter um ritmo constante indefinidamente.

Resolveria você ser Ágil somente a cada duas semanas ou a cada dois projetos? Basicamente este princípio pede práticas que evitem o estresse excessivo e a exaustão, os quais afetam a qualidade de vida e também os próprios projetos. Então, basicamente:

  • Não empurre para o time mais trabalho do que ele é capaz de executar no período.
  • Não conte com horas extras para a conclusão de um projeto.

Além do mais, é comum um projeto ser concluído no prazo e com sucesso graças a horas extras? É comum o trabalho executado no sábado e domingo render tanto quanto o que foi executado na segunda e terça-feira?

9. Contínua atenção à excelência técnica e bom design aumenta a agilidade.

Difícil fazer um cliente feliz com um software instável e cheio de bugs ou que não ataque suas necessidades, bem como é difícil implementar novos recursos rapidamente neste tipo de software. Um design ruim também dificulta evolução ou mudanças no software. De modo que este princípio ajuda a suportar os 3 primeiros princípios, e algumas práticas para suportá-lo são:

  • Aprimorar-se profissionalmente, sempre (estudo autodidata, cursos, congressos, exercitar novas maneiras de fazer a mesma coisa, etc.).
  • Escrever código de qualidade para que possa ser alterado sem sofrimento.
  • Refatorar para aumentar a qualidade do código. Você também pode refatorar o design e mesmo a arquitetura.
  • Manter a arquitetura simples, muito simples, tão simples quanto possível.
  • Não descuidar do design:

É comum começar com um design lindo e depois degringolar. Manter um bom design deve ser aceito como uma batalha constante e interminável.

  • Testar sempre todo o sistema para aumentar a garantia de que o que estava funcionando permanece funcionando. Aqui a agilidade só é viável se a maior parte dos testes forem automatizados.

10. Simplicidade – a arte de maximizar a quantidade de trabalho não realizado – é essencial.

Este é, na minha visão, o princípio mais difícil para o time e cliente suportarem. Mas, como o próprio princípio profetiza, ele é essencial. Sua não observação simplesmente impede a agilidade.

Maldita vontade que nos dá de fazer algo que não precisa ser feito, pelo menos não agora. É normal: somos grandes profissionais, queremos exercitar nossa criatividade, queremos averiguar se nossa grande ideia é realmente maravilhosa, queremos surpreender o cliente. Eis algumas práticas para evitar esta síndrome e ajudar a suportar este princípio:

  • Não desenvolva nada além do que foi acordado com o cliente:

O cliente será positivamente surpreendido com um recurso que foi implementado exatamente como ele queria. O “plus” no qual você viu uma oportunidade de impressionar provavelmente será completamente ignorado por ele; é melhor investir em entender com precisão o que ele quer, em vez de tentar adivinhar e fazer por conta própria aquilo que você acha que ele pode querer.

  • Ajude o cliente a enxergar, em cada requisito, a oportunidade de quebrar em requisitos menores, até ter requisitos realmente pequenos:

Não implemente todo um processo do sistema como sendo um único requisito. Validações, caminhos alternativos, interface rica, automação de processos subsequentes, API para integração, escalabilidade… são exemplos de recursos que podem ser feitos depois que o cliente der o feedback sobre a funcionalidade básica. Com o feedback do cliente você descobrirá que nem precisará implementar alguns destes recursos.

  • Mantenha uma visão sistêmica mas não a deixe lhe perturbar:

O software não deve receber agora o código necessário para requisitos futuros. Ao invés, mantenha um código de qualidade, com um design bom e simples, e testes automatizados, para que você possa implementar mais facilmente estes requisitos quando eles realmente chegarem, se chegarem.

  • Não otimize adiantadamente:

É claro que você, como bom desenvolvedor, vai evitar algoritmos com elevados custos desnecessários e vai fazer um software naturalmente rápido. Mas só invista em otimizações complexas quando o seu valor para o negócio puder ser validado pelo cliente.

11. As melhores arquiteturas, requisitos e designs emergem de equipes auto-organizáveis.

A primeira parte deste princípio diz que boa arquitetura, requisitos e design emergem em vez de serem projetados adiantadamente. A segunda parte propõe que esse trabalho é melhor realizado por times auto-organizáveis. Eis algumas práticas para suportar este princípio:

  • Suporte o quinto princípio.
  • Monte um time que contenha todas as competências necessárias para concluir o trabalho.
  • A distribuição de tarefas é de responsabilidade do próprio time, coletivamente, ou seja: não pode ser feita por um gestor.
  • Forneça coaching e mentoring ao time em vez de decidir como eles devem executar seu trabalho.

12. A intervalos regulares, o time reflete sobre como se tornar mais eficaz e então refina e ajusta seu comportamento de acordo.

  • Estabeleça um evento formal e regular para realizar uma retrospectiva, em grupo, sobre como o time vem interagindo e sobre os processos de trabalho.
  • Introduza novos métodos.
  • Elimine processos que não estejam mais agregando valor.
  • Crie tarefas e nomine responsáveis para a solução de problemas que estejam afetando o trabalho.
  • Não abra mão destas retrospectivas, mesmo quando parecer que sua forma de trabalho atingiu o máximo da eficiência e eficácia.

Por enquanto seria isso. As técnicas que viabilizam estas práticas serão temas de posts futuros.

Deixe um comentário.

A essência do Ágil

Publicado: 12 de novembro de 2013 em Uncategorized
Tags:, ,

Direto ao ponto: a essência do desenvolvimento Ágil de software é o ciclo reduzido de feedback do cliente.

Podemos dizer que estamos inseridos em um contexto Ágil de desenvolvimento de software quando entregamos ao cliente software de valor a intervalos máximos de 2 meses. Podemos dizer que estamos obtendo sucesso com o desenvolvimento ágil de software quando na maioria destas entregas o cliente fica satisfeito com o que recebe.

Feedback do cliente não significa ele dizer se gostou ou não. Feedback do cliente serve para orientar continuamente o desenvolvimento do produto. A orientação que o cliente fornece é afetada pela entrega anterior, e afetará a entrega seguinte – e este é o ciclo de feedback.

O primeiro princípio do Manifesto Ágil não deixa dúvida sobre a essência do Ágil:

Nossa maior prioridade é satisfazer o cliente através da entrega adiantada e contínua de software de valor.

O terceiro princípio do Manifesto Ágil esclarece um pouco o que é software de valor e determina o quão reduzido é o ciclo de feedback:

Entregar software funcionando frequentemente, de duas semanas a dois meses, com preferência para o período mais curto.

Os demais princípios e mesmo os valores (texto principal do Manifesto Ágil), apresentam a cultura proposta para se alcançar este objetivo.

Erros comuns que fazem ficar de lado o princípio essencial:

Leia o resto deste post »

Não herdar é libertador

Publicado: 23 de setembro de 2013 em Uncategorized
Tags:

Quando eu me iniciei no mundo da Programação Orientada a Objetos eu ficava muito fascinado com a possibilidade de herdar classes e procurava oportunidades de fazer isso. Sempre lembrava que pessoa herdava de mamífero que herdava de animal, do qual herdava também o pássaro, do qual herdavam a galinha e o gavião, sendo que mamífero emite som e cada classe concreta implementa o seu próprio som.

Menos a girafa, que se você invocar o método “emitir som” decerto é lançada uma “not implemented exception” ou a girafa se comporta de uma maneira muito louca e você vai ter que depurar pra entender.

Mais complexo ainda é o momento em que você descobre que galinha e gavião também emitem som e resolve subir o método abstrato “emitir som” da classe Mamífero para a classe Animal. E daí emitir som aparece na classe minhoca a qual, mesmo que emita algum som, não é relevante para o seu negócio.

Há alguns anos comecei a perceber que herdar estava mais me atrapalhando que ajudando. Leia o resto deste post »

Pelo menos não com muita força.

Conheci times onde o tempo todo o cliente (geralmente interno e de alto nível hierárquico) trazia uma inversão de prioridades e o time ou algumas pessoas do time paravam tudo que estavam fazendo e se punham a trabalhar na nova prioridade, que mudava novamente na semana seguinte, e a história se repetia. Leia o resto deste post »

Antes de escrever este texto eu fui procurar algumas opiniões já publicadas sobre isso. Não consegui ir muito longe porque me deparei com um verdadeiro show de horrores e fiquei paralisado. Encontrei uma verdadeira tese ensinando a comentar código fonte!

Quando comecei a ler eu achei que o cara estava de brincadeira, que era uma pegadinha e que no final ele esclareceria tudo. Mas não! Leia o resto deste post »

Pergunta: “Qual destas duas opções é melhor?”
Resposta: “Depende.”
Pérrrrnnnn (onomatopeia de campainha ruidosa). Resposta errada.
O mundo está muito mais complexo do que era há poucas décadas atrás. “Depende” já foi uma resposta de vanguarda, que demonstrava o cuidado de não opinar de maneira simplista sobre um cenário possivelmente complexo.  Leia o resto deste post »