Os desenvolvedores podem testar seu próprio código?

Por esses dias, estive relendo os posts sobre como são conduzidas as atividades de testes no google, entre outras coisas eles falam sobre os papéis, funções e de que maneira a qualidade de software é conduzida dentro da empresa.

No terceiro post da série, diversas afirmações chamaram a minha atenção e valem a nossa reflexão:

“Who better to do all that testing than the people doing the actual coding? Who better to find the bug than the person who wrote it? Who is more incentivized to avoid writing the bug in the first place?”

A partir dessa afirmação podemos ver que estamos passando por uma grande transição na engenharia de software. Onde há vários anos as empresas dão uma ênfase cada vez maior aos aspectos relacionados à qualidade de software e diversas estratégias surgiram e vêm sendo utilizadas para a organização das equipes e divisão das tarefas.

Porém, o que mais me chama atenção nessa primeira afirmação é como a idéia a que estava acostumado, de que precisamos de pessoas com dois perfis diferentes para testar e desenvolver um software está ficando ultrapassada.

Make it or Break it

Cada vez mais precisamos unir as duas disciplinas que se completam para assim entregar produtos de maior qualidade.

Claro, que para que isso aconteça é necessária uma mudança cultural e comportamental. Abandonarmos os antigos conceitos de que desenvolvedores não conseguem enxergar as falhas em seu próprio código, não gostam e não querem testar e tornar tudo em uma única tarefa.

Diversos benefícios podem emergir dessa tendência, como: detecção de defeitos cada vez mais cedo, maior liberdade para o engenheiro de teste focar em aspectos não funcionais, fluxos de integração e outros pontos que fogem a unidade do desenvolvedor, etc.

  “quality is more an act of prevention than it is detection”

Desse modo, técnicas como o TDD podem ser excelentes caminhos para eliminar essa separação entre testes e desenvolvimento. Ajudando a tornar a qualidade cada vez mais um ato de prevenção do que detecção.

” Testing must be an unavoidable aspect of development and the marriage of development and testing is where quality is achieved”

É óbvio, que existem diferenças entre os diversos tipos de projetos e na realidade de cada uma das empresas, onde cada um possui necessidades diferentes as quais precisam ser avaliadas e planejadas.

Está cada vez mais claro o caminho para produzirmos softwares de maior qualidade, testes e desenvolvimento como uma só tarefa, apresentando um grau de automação cada vez maior. Para seguirmos esse caminho várias mudanças são necessárias tanto nas pessoas, como nos processos e ferramentas.

E vocês o que acham? É esse o caminho a ser seguido?

Acompanhando projetos através do gráfico de Burndown

Existem distintas formas de se acompanhar o andamento de um projeto. Praticamente cada modelo de gerenciamento sugere sua própria forma de seguir de perto como anda o desenvolvimento das tarefas. Gráfico de Gantt, gráfico focado em produtividade do Kanban e etc… são apenas algunas exemplos do que é utilizado hoje no acompanhamento de projetos em TI. Acredito que a maioria dos leitores esteja acostumada e familiarizada com os gráficos de Gantt. Apesar de serem bastante difundidos e utilizados mundo afora, pessoalmente julgo que esse método de acompanhamento funciona muito bem quando o foco são datas de entrega ou planejamentos de longo prazo. Quando focamos no acompanhamento da produtividade de uma equipe no dia-a-dia, o gráfico burndown do SCRUM e o baseado na produtividade do Kanban se mostram muito mais robustos e evidentes.

Ilusao de Otica
A principio se vê o rosto de uma mulher, porém ao observar mais atentamente, também se vê um saxofonista.

Para este post, vou focar no gráfico de burndown. Neste gráfico, o eixo horizontal representa os dias de duração de uma sprint e o eixo vertical a quantidade de tarefas que ainda não foram cumpridas dentro do espaço de tempo determinado. Dessa forma, todos os dias a gerência da equipe pode verificar a produtividade do dia anterior e qual a meta que precisa ser alcançada para que todas as tarefas que ainda restam consigam ser finalizadas no prazo.

Acredito que a maior dificuldade para a equipe de gestão de um projeto SCRUM é saber interpretar os dados que lhe são mostrados no gráfico e como repassar essas informações preciosas para o product owner do projeto ou o cliente em questão. Sprint passada enfrentei um problema similar. Algumas alterações no burndown não foram bem interpretadas pelo product owner do projeto. Percebi que, apesar de atingir muito bem seu objetivo, algum tipo de informação relevante estava faltando ali. É como se a informação estivesse na frente dos nossos olhos, mas a nossa forma de pensar estivesse bloqueando o real entendimento.

Decidi então adicionar uma nova curva ao burndown. Ao invés de mostrar somente a curva da quantidade de tarefas “queimadas” durante os dias da sprint, coloquei também uma nova curva onde apresentávamos a evolução do número total de tarefas. Pode parecer estranho para alguns, mas no SCRUM as tarefas que devem ser executadas em cada iteração podem variar de acordo com a demanda. Durante uma sprint, podemos tanto remover como adicionar novas tarefas e justamente essa visibilidade foi a que senti que estava faltando no burndown do meu projeto. Uma descida mais suave no gráfico de produtividade não significa que a equipe teve uma baixa produtividade, mas sim a influência do incremento do número de tarefas a serem executadas naquele período.

Gráfico de Burndown exibindo a curva com a evolução do número total de Tarefas

Como testar quando o software não tem botões?

Mencionei anteriormente da importância, para uma pessoa que trabalha com testes de software, de se ter conhecimentos sobre como desenvolver e ter uma visão do ambiente em que o software opera. Tais conhecimentos podem ser considerados facultativos para diversos profissionais de teste, mas em certos casos se tornam obrigatórios.

O que acontece, por exemplo, quando não existe uma UI para testar? Este cenário acontece quando o software desenvolvido trata-se de uma API (Application Programming Interface). O teste de API requer o desenvolvimento de uma aplicação que simule o uso real das chamadas.

Uma vez que se trata de testes de API, Josh Poley menciona que muitas técnicas diferentes serão sugeridas, dependendo do testador. Entretanto, o mesmo apresenta uma situação que funcionou bem para os testes do Sistema Operacional do Xbox.

Participei de dois projetos em que aplicações eram desenvolvidas para que o produto principal pudesse ser testado. No primeiro, o produto desenvolvido continha implementações de JSRs para um emulador de J2ME. Nos testes, então, eram criadas aplicações J2ME, que tinham a proposta de avaliar as classes definidas em cada especificação.

No outro projeto, foi criada uma API para comunicação com determinadas impressoras. Nesta aplicação, uma das funcionalidades era o envio de uma linguagem intermediária para API, que gerava um comando (PCL / PJL / HP-GL) correspondente e mandava para impressão. Para tal funcionalidade, era necessário o conhecimento das entradas e saídas esperadas (tanto da linguagem intermediária como do comando enviado para impressora). Os testes foram desenvolvidos em C++, simulando uma aplicação real que utilizaria a API.

Mas antes de começar a desenvolver os testes, é necessário dedicar um tempo para o planejamento do que será testado. Dependendo da complexidade da aplicação, um documento de testes pode auxiliar nessa atividade.

Já na criação dos testes, deve-se pensar como o ambiente pode ser preparado para identificar certas situações, como por exemplo, o uso de um arquivo corrompido ou a ausência de conexão de rede. A atividade de forçar falhas, em uma aplicação, pode ser considerada bem interessante. Uma vez que se detêm conhecimento para criar testes que forcem defeitos, o entusiasmo é enorme. Mas atenção, a prioridade deve ser dada ao fluxo principal, primeiro crie testes para as situações em que se espera que o software funcione.

Alguns cenários são mais complexos de serem testados. Casos como atualização de estruturas de dados, modificação de certos recursos e chamadas da API que não possuem retorno, necessitam de alguma forma de verificação. Uma função que não tem retorno, por exemplo, deve mudar de alguma forma o estado da aplicação. Em um teste a uma chamada que remove algum valor de uma lista e não tem valor de retorno, pode ser necessário usar de chamadas para percorrer a lista e verificar os valores, tendo assim certeza de que o valor solicitado foi removido.

James Whittaker e Alan Jorgensen relataram que, em teste de API, alguns problemas interessantes para os testadores são: garantir que a aplicação varia os parâmetros das chamadas de modo a expor possíveis falhas; gerar combinações interessantes de parâmetros; configurar o ambiente externo e os dados internos que afetam a API; variar a seqüência de chamadas da API para exercitar de várias formas as funcionalidades.

No blog de testes da Google, em um dos posts é mencionado que testes de chamadas isoladas da API não conseguem simular o cenário real. Para uma calculadora, testar as chamadas isoladamente pode ser efetivo. Mas para aplicações em que a ordem das chamadas pode interferir no resultado, devem ser testadas tanto as chamadas isoladas como combinações entre elas.

A organização dos testes é importante para facilitar a manutenção. Códigos bem escritos podem auxiliar no entendimento da equipe sobre o que está sendo testado. Então, caso possível, o uso de um guia com padrões de codificação é bem vindo. Dessa forma, a equipe irá se acostumar a escrever de forma que todos entendam. Quando o cenário de teste é complexo, a documentação no código ajuda a entender do que se trata.

A qualidade do código de teste pode influenciar diretamente na qualidade do produto testado. Erros nos testes podem omitir falhas importantes nos produtos.

Mas quem vigia os vigilantes? Quem testa o código de teste? Como garantir que os testes estão corretos? Quando se escreve códigos de teste, a atenção deve ser dobrada. Com isso é importante a cultura de revisão de código, programação em pares ou alguma outra forma que auxilie na diminuição de erros. Criar log da execução pode ajudar na identificação de problemas.

Pelo fato de os testes serem parecidos, a prática do copy & paste vai parecer tentadora. Mas evitar esta prática pode evitar vários problemas. Recomendo uma apresentação de Borba, para ter a visão dele sobre código duplicado. E, se em algum momento achar que o código de teste pode melhorar, melhore!

Nos projetos que trabalhei, a padronização dos testes foi um ponto importante, para transferência de conhecimento aos que precisavam entender o que estava sendo testado. Para o emulador de J2ME, foi criado um framework no qual as aplicações de teste deveriam ser desenvolvidas. Em ambos os projetos, certos testes precisavam de uma confirmação visual, mas ainda assim foi possível avaliar tais cenários, só demandava uma confirmação do testador. Nos testes foram contempladas variações de parâmetros e combinações de chamadas. Também foram utilizadas algumas técnicas de teste como pairwise, classe de equivalência, valores limite, etc. Os testes eram documentados e tanto o documento como o código passavam por um processo de inspeção. Sempre que possível, os testes eram projetados para necessitarem da menor intervenção humana, realizando o maior número de testes ao apertar um único botão.

O que fazer quando você descobre que seu código está uma merda?

Não se desespere, eventualmente você vai descobrir que o código que você está trabalhando a dias está uma merda 🙂 Geralmente é preciso outra pessoa para lhe mostrar que o seu código não está (até por esse motivo que práticas de revisão de código e pair programming são tão importantes). Eventualmente você pode se dar conta sozinho que seu código está ruim, mas é muito mais difícil e improvável.

A boa notícia é a seguinte: Sendo você um bom programador ou não, vai chegar um momento que você vai fazer um código que você não tem orgulho (mas PRECISAMOS nos orgulhar do código que produzimos). Então, se acalme e vamos ao que interessa, desfazer a merda 😛

Já vi muita coisa sobre eXtreme Programming e seus valores, e um valor em especial sempre me pareceu meio obscuro, mas agora chegou um momento onde esse valor é evidenciado. Nesse momento a PRIMEIRA coisa que você precisa é:

CORAGEM! Para admitir que seu código poderia estar muito melhor, e CORAGEM para correr atrás do prejuízo e modificar o que for preciso para melhorar o código que você tinha feito.

Tudo bem… Você já percebeu que o seu código não está bom, e teve coragem de admitir isso e partir em direção de melhorar aquele código. E a segunda coisa, óbvia, que você precisa fazer é:

AGIR! Mudar o panorama da situação! Geralmente algumas mudanças pequenas e a qualidade do seu código já melhora sensivelmente. É claro que você precisa saber COMO melhorar seu código.

A grande maioria das vezes, quando você identifica o problema no seu código, mas também já visualiza as modificações necessárias para melhorá-lo, então é simplesmente seguir essas direções. De qualquer forma, aqui deixo uma listinha com algumas sugestões que são sempre úteis para melhorar seu código:

  • Morte ao código duplicado (mantra de @luizborba).
  • Remover referências erradas/desnecessárias (diminuir acoplamento).
  • Manter cada classe/método com uma única responsabilidade (aumentar a coesão).
  • Verificar se algum padrão de projeto resolve seu problema.
  • Atenção a muitos IFs e SWITCH  CASES, eles são grandes candidatos a um refactoring.
  • etc, etc…

é isso! errar é normal, o importante é consertar! 🙂

A falta de inovação e empreendedorismo

Quando eu estava pensando num título para este post eu imaginei algo muito extenso, que com certeza não cairia bem para um título de post (talvez ficasse melhor em um título de uma tese de doutorado :P). Seria algo mais ou menos assim:

“A Falta de inovação e educação empreendedora e a distância entre a academia e a indústria: De quem é a culpa?”

Mas decidi por um título mais curto e mais objetivo. Há uns tempos atrás eu comecei a ler Pai Pobre Pai Rico, onde o autor fala um pouco dentro do enredo do livro em como somos ensinados a NÃO EMPREENDER. Somos educados (não somente pelos nossos pais, mas pela estrutura de ensino que temos em nossas escolas e universidades) a estudar, estagiar, trabalhar duro, se capacitar para um dia sermos CONTRATADOS por uma boa e grande empresa. Que ironia (Brasil em especial). O foco deveria ser completamente diferente, deveríamos ser educados para estudar, nos capacitar e trabalhar duro com o objetivo de CRIAR UMA GRANDE EMPRESA/PRODUTO (ou mesmo COMPRAR UMA GRANDE EMPRESA/PRODUTO) e CONTRATAR pessoas capacitadas para trabalhar nessas EMPRESAS. Uma diferença abismal.  Recentemente também acompanhei uma série de 19 posts do Prof. Silvio Meira (do Centro de Informática da UFPE) sobre educação empreendedora, onde ele também trata desse problema (recomendo a leitura).

Acredito que eu faça parte desse time de pessoas que foram criadas e ensinadas a batalhar para conseguir um bom emprego, mas diante de uma realidade de mundo atual, não podemos nos limitar a isso somente.

Mas finalmente, como corrigir um modelo desses impregnado em uma geração de pessoas que foram criadas e ensinadas a ser empregados? Como aprender a ser donos? De quem é a culpa desse modelo em parte “fracassado” que temos no Brasil hoje?

Parece-me um deadlock: A indústria brasileira não inova porque não tem quem invista e o os detentores do dinheiro não investem porque não existem muitas iniciativas de inovação. A universidade (estou falando mais da área de tecnologia da informação) não faz pesquisa relevante para o cenário (tempo e contexto) do Brasil e por isso o mercado não usufrui dessas pesquisas, e essa demanda não parte do mercado, pois a universidade está preocupada em publicar artigos (dentre outras, essa é uma das principais formas de avaliar um professor universitário aqui no Brasil). As pessoas não empreendem porque precisam estagiar e conseguir um emprego, e com isso as possibilidades de criar algo relevante para o mercado/indústria começa a diminuir. Os problemas são muitos, mas precisamos romper essa dimensão dos problemas e entrar de vez em outra realidade onde somos todos empreendedores em potencial [nossa área em especial permite que empreendamos com baixíssimo investimento inicial, e uma perfeita característica também de detectar potencial rapidamente de acordo com a reação do público.]

A única resposta (e que pode soar um tanto quanto clichê) que me vem a mente depois disso tudo é: A mudança depende de cada um, e de um conjunto de rupturas individuais com esses conceitos existentes.

Você pode se perguntar: Como eu vou fazer isso? O que eu tenho que fazer para romper com essa realidade?

Eu também me pergunto isso! Aguarde, no próximo post vamos discutir mais sobre isso…