A Relação Entre Desenvolvimento Orientado a Testes e Qualidade ...
A Relação Entre Desenvolvimento Orientado a Testes e Qualidade ...
A Relação Entre Desenvolvimento Orientado a Testes e Qualidade ...
You also want an ePaper? Increase the reach of your titles
YUMPU automatically turns print PDFs into web optimized ePapers that Google loves.
A <strong>Relação</strong> <strong>Entre</strong> <strong>Desenvolvimento</strong> <strong>Orientado</strong> a <strong>Testes</strong><br />
e <strong>Qualidade</strong> de Software<br />
Cássio L. Ribeiro 1<br />
1 Instituto de Informática – Pontifícia Universidade Católica de Goiás (PUC Goiás)<br />
Goiânia – GO – Brasil<br />
cassio.landim@gmail.com<br />
Abstract. This paper shows how the consequences of the use of test-driven development<br />
affect the quality of software. It lists some situations that negatively<br />
affect the quality of software, such as the lack of unit testing and presence of<br />
disorganized code. It demonstrates how the use of test-driven development mitigates<br />
or eliminates the occurrence of these situations. Finally, it highlights other<br />
important quality factors that the test-driven development does not resolve properly.<br />
Resumo. O presente artigo evidencia de que maneira as consequências do emprego<br />
do desenvolvimento orientado a testes afetam a qualidade de um software.<br />
São listadas algumas situações que afetam negativamente a qualidade de um<br />
software, como a ausência de testes unitários e a presença de código desorganizado.<br />
Demonstramos como o emprego do desenvolvimento orientado a testes<br />
ameniza ou elimina a ocorrência dessas situações. Ao final, evidencia outros<br />
fatores importantes para a qualidade, que o desenvolvimento orientado a testes<br />
deixa a desejar.<br />
1. Introdução<br />
A natureza ubíqua da computação moderna, software e infraestrutura de informação e,<br />
ainda, a demanda crescente por automação e conveniência pela sociedade moderna tem<br />
gerado constante aumento de tamanho e complexidade dos sistemas de software modernos.<br />
Este incremento em tamanho e complexidade acaba por causar consequências nãointencionais<br />
em termos de gerar problemas de qualidade [Tian 2005].<br />
A alta complexidade e o amplo escopo dificultam a prevenção ou eliminação de<br />
problemas de software e seus impactos negativos relacionados; consequentemente, várias<br />
atividades de garantia da qualidade são realizadas para prevenir e/ou eliminar certas classes<br />
de problemas, ou ainda, para reduzir a probabilidade ou severidade de tais impactos<br />
quando não se pode evitá-los [Tian 2005].<br />
As metodologias ágeis emergiram da necessidade de se minimizar os riscos do<br />
desenvolvimento de softwares. Essas metodologias evoluíram como parte de uma reação<br />
contrária aos processos de desenvolvimento utilizados na época, caracterizados como<br />
“pesados”, burocráticos, micro-gerenciados e com uma alta tendência a se produzir documentos<br />
formais [Langr 2005].<br />
Como o desenvolvimento orientado a testes (Test-Driven Development - TDD) é<br />
uma prática fundamental da Programação Extrema (eXtreme Programming - XP), que está
começando a ganhar uma ampla aceitação entre as entidades desenvolvedoras de software,<br />
este estudo visa evidenciar de que maneira o emprego do TDD afeta a qualidade de um<br />
software.<br />
2. Conceitos de <strong>Qualidade</strong> de Software e TDD<br />
2.1. <strong>Qualidade</strong> de Software<br />
[Tian 2005] coloca a qualidade sob a perspectiva das expectativas dos usuários de software.<br />
Em geral, o que as pessoas esperam como qualidade em um sistema de software<br />
é que eles façam o que foram programados para fazer. Em outras palavras, eles devem<br />
fazer a coisa certa, ou seja, eles devem executar suas tarefas específicas corretamente ou<br />
satisfatoriamente.<br />
Comportamentos inesperados se manifestando na forma de defeitos, podem afetar<br />
profundamente a qualidade de um software, porém, este não é o único fator que determina<br />
a qualidade. Segundo [Friedman 2009], as metodologias ágeis tratam o processo<br />
de incremento da qualidade como um exercício de “eliminação do desperdício” onde o<br />
conceito de “desperdício” assume o mesmo significado no contexto da manufatura lean<br />
1 [James P. Womack 1990], onde um produto de alta qualidade é aquele que previne o<br />
desperdício do esforço investido pelo usuário no processo de produzir algo de valor.<br />
[Friedman 2009] identifica três fatores principais de desperdício que reduzem a<br />
qualidade de um software. O primeiro e mais comum são os defeitos. Estes podem ser<br />
categorizados como ocorrências onde o sistema se comporta de forma inesperada. Enquanto<br />
o desenvolvedor espera que o sistema se comporte de certa maneira, o comportamento<br />
exibido é diferente devido a defeitos inseridos na fase de codificação. O problema<br />
é que quando um defeito afeta o usuário, em casos menos graves, pode forçar o usuário<br />
a repetir alguma ação, enquanto que em casos extremos pode causar uma perda substancial<br />
de dados que irá requerer um esforço significativo para serem reproduzidos (quando<br />
possível).<br />
O segundo fator está ligado a problemas de usabilidade, onde um produto de difícil<br />
uso causa um desperdício de esforço. Em casos extremos, os usuários fazem uso<br />
de caminhos alternativos para atingirem seus objetivos; entretanto, em vários casos, os<br />
usuários empregam a forma incorreta de execução ao invés de perceber que a qualidade<br />
atual do sistema é insuficiente para atender seus propósitos.<br />
O terceiro fator, funcionalidades do sistema que não são utilizadas, também representam<br />
um fator de desperdício. Além de inúteis, retardam e dificultam o uso do sistema.<br />
Esta é a forma de desperdício que mais despende esforços para ser localizada e eliminada.<br />
2.1.1. Verificação versus Validação<br />
Verificação é provar que o produto atende corretamente os requisitos especificados em<br />
atividades prévias durante o ciclo de vida de desenvolvimento, já a validação checa que o<br />
1 O termo lean foi cunhado ao final da década de 80 em um projeto de pesquisa do Massachusetts Institute<br />
of Technology (MIT) sobre a indústria automobilística mundial. A pesquisa revelou que a Toyota havia<br />
desenvolvido um novo e superior paradigma de gestão nas principais dimensões dos negócios (manufatura,<br />
desenvolvimento de produtos e relacionamento com os clientes e fornecedores).
sistema atende os requisitos do cliente ao final do ciclo de vida. Tradicionalmente, teste<br />
de software tem sido considerado um processo de validação, isto é, uma fase do ciclo de<br />
vida [Lewis 2004].<br />
2.1.2. <strong>Qualidade</strong> Externa e Interna<br />
<strong>Qualidade</strong> externa é a qualidade medida pelo cliente, e qualidade interna é a qualidade<br />
medida pelas pessoas que possuem acesso ao código, como é o caso dos programadores<br />
e arquitetos.<br />
As metodologias ágeis pregam que a qualidade interna de um software não é negociável,<br />
ela deve ser fixada como ótima durante todo o ciclo de vida do software. Algumas<br />
equipes, na esperança de reduzir o prazo de entrega, sacrificam temporariamente<br />
a qualidade interna na esperança de que a qualidade externa não sofrerá um grande impacto.<br />
Esta estratégia é muito tentadora em um cenário em curto prazo. Porém, eventualmente<br />
problemas de qualidade interna o alcançarão e tornarão aquele software proibitivamente<br />
caro de manter ou mesmo incapaz de alcançar um nível competitível de qualidade<br />
externa [Beck 1999]. Este fenômeno é conhecido como débito tecnológico. Segundo<br />
[Friedman 2009], tentar trocar a qualidade por velocidade é uma das estratégias mais errôneas<br />
existentes.<br />
2.2. <strong>Testes</strong> Unitários<br />
De acordo com [Lewis 2004], teste unitário é a escala mais “micro” de testes para testar<br />
funções particulares ou módulos de códigos. Tipicamente são feitos pelo programador e<br />
não por testadores, já que requerem um conhecimento detalhado do código e do design<br />
interno do software. Nem sempre são fáceis de se implementar a não ser que a aplicação<br />
tenha uma arquitetura muito bem desenhada com código organizado.<br />
[Bellware 2009] acredita que quando testes são escritos após o código de produção<br />
já estar pronto, na realidade o que se está fazendo é um tipo de engenharia reversa, que é<br />
uma forma de engenharia de que exige uma grande quantidade de esforço. Exige muito<br />
esforço porque ao se escrever os testes neste momento, o programador precisará ler todo<br />
o código, entender o que ele faz e adivinhar como escrever um teste para este código.<br />
Os testes produzidos desta maneira acabarão, provavelmente, não servindo para cobrir o<br />
mínimo necessário dos caminhos alternativos de execução do código.<br />
2.3. <strong>Desenvolvimento</strong> <strong>Orientado</strong> a <strong>Testes</strong><br />
As metodologias ágeis já se tornaram populares na área de desenvolvimento de software.<br />
A cada dia, mais empresas as adotam para amenizar problemas de qualidade e de entrega<br />
de produtos que agreguem o valor real aos seus clientes.<br />
Algumas metodologias ágeis como Scrum tem foco em práticas gerenciais e organizacionais,<br />
enquanto XP tem foco maior em práticas de programação. O TDD é uma<br />
das práticas que fazem parte do núcleo do XP [Beck 1999] que entra em cena durante as<br />
fases de desenho e codificação de um software.<br />
XP é uma metodologia ágil e leve para equipes de desenvolvimento de software<br />
de tamanho pequeno à médio que lidam com requisitos vagos ou que mudam rapidamente
[Beck 1999]. XP procura melhorar um projeto de software através da comunicação, simplicidade,<br />
feedback, respeito e coragem. Dá ênfase no trabalho em equipe, permitindo que<br />
os desenvolvedores respondam com confiança às mudanças de requisitos dos clientes, até<br />
mesmo tardiamente no ciclo de vida do projeto.<br />
Scrum é um processo ágil para o desenvolvimento de projetos, sendo mais adequado<br />
para projetos onde os requisitos estão mudando ou emergindo rapidamente. É mais<br />
focado nas pessoas e na forma como elas deverão interagir entre si para atingir um objetivo<br />
comum.<br />
[Schwaber 2004] afirma que o Scrum baseia todas as suas práticas em uma estrutura<br />
de processo incremental e iterativo. Esta estrutura é formada por iterações de<br />
atividades de desenvolvimento que ocorrem uma após a outra, sendo que a saída de cada<br />
iteração é um incremento do produto. Dentro desta iteração ocorrem várias iterações mais<br />
curtas (inspeções diárias), onde os indivíduos membros do time se encontram para inspecionar<br />
as atividades dos outros membros e fazerem as adaptações necessárias. Uma lista<br />
de requisitos guia a iteração. Este ciclo se repete até que o projeto termine.<br />
Ao início de cada iteração, o time analisa o que deve ser feito. Ele então escolhe o<br />
que acredita que possa se tornar um incremento de funcionalidade potencialmente entregável<br />
ao final da iteração. O time é deixado a sós para fazer seu melhor esforço pelo resto<br />
da iteração. Ao final da iteração, o time apresenta o incremento de funcionalidade que<br />
construiu para que os patrocinadores do projeto possam inspecioná-lo e fazer ajustes para<br />
as próximas iterações do projeto. O processo criativo durante as iterações é o coração da<br />
produtividade do Scrum.<br />
Já o TDD é uma prática que visa aumentar a velocidade da entrega de produtos<br />
através da simplificação das atividades de desenho de software. [Koskela 2008] resume a<br />
filosofia do TDD em uma frase – somente escreva código para fazer um teste falho passar.<br />
[Astels 2003] define o TDD como sendo um estilo de desenvolvimento onde:<br />
• Uma suíte exaustiva de testes de programadores é mantida;<br />
• Nenhum código entra em produção a não ser que tenha testes associados;<br />
• Os testes são escritos antes;<br />
• Os testes determinam que código precise ser escrito.<br />
2.3.1. Ciclo Teste-Codificação-Refatoração<br />
TDD é uma técnica de desenho e desenvolvimento que nos ajuda a construir um sistema<br />
de forma incremental, com a consciência de que nunca nos afastamos de uma baseline<br />
funcional e entregável. O ciclo Teste-Codificação-Refatoração é quem dita o ritmo do<br />
desenvolvimento através de pequenos e controlados passos. A sequência clássica que nos<br />
foi ensinada, onde primeiramente produzimos o desenho, implementamos o desenho, e<br />
só então testamos o software em busca dos defeitos, é totalmente invertida quando comparada<br />
ao ciclo Teste-Codificação-Refatoração.<br />
Primeiramente escrevemos o teste, depois escrevemos o código para fazer este<br />
teste passar e então passamos para a fase de desenho. Esta fase de desenho é diferente da<br />
fase de desenho tradicional. No TDD ela é chamada de refatoração, onde o desenho atual<br />
do código é transformado em um desenho melhor.
O ciclo Teste-Codificação-Refatoração também pode ser chamado de ciclo<br />
Vermelho-Verde-Refatoração [Astels 2003]. O vermelho simboliza a fase inicial do ciclo<br />
TDD onde o teste escrito falha. Ele falha porque o sistema não está funcional; ele<br />
não possui as funcionalidades que desejamos que ele tenha. Em alguns ambientes de<br />
desenvolvimento, essa falha é evidenciada através da exibição de uma barra vermelha.<br />
Na segunda fase implementamos as funcionalidades que faltavam para fazer todos<br />
os testes passarem, ou seja, os testes que já existiam e o novo teste recém-introduzido.<br />
Neste momento, a barra visual deve se tornar verde. Somente se passa para a próxima<br />
etapa quando nenhum teste estiver falho.<br />
Na última parte do ciclo é feita a refatoração. Refinamos o desenho do código<br />
sem alterarmos seu comportamento externo, mantendo todos os testes passando e a com<br />
a barra visual exibindo a cor verde.<br />
3. A <strong>Relação</strong> entre TDD e <strong>Qualidade</strong><br />
<strong>Qualidade</strong> não pode ser alcançada através da avaliação de um produto já feito. O objetivo,<br />
portanto, é prevenir defeitos de qualidade ou deficiências em primeiro lugar, tornando os<br />
produtos avaliáveis através de medidas de garantia de qualidade [Lewis 2004].<br />
[Beck 1999] cita alguns exemplos de riscos relacionados ao desenvolvimento de<br />
software. Dos riscos citados, dois estão diretamente ligados a qualidade de software e<br />
podem ser tratados através da utilização de TDD:<br />
• Taxa de defeitos – o software é colocado em produção mas a taxa de defeitos é tão<br />
alta que ele acaba não sendo utilizado. TDD eleva a validação de um software a<br />
um patamar superior, testando-o função por função;<br />
• Deterioração do sistema – o software é colocado em produção com sucesso, porém<br />
após algum tempo o custo de se fazer modificações ou a taxa de defeitos aumenta<br />
tanto que o sistema precisa ser substituído. TDD mantém o programador focado<br />
na solução, de forma que o software não fica carregado de códigos desnecessários,<br />
duplicados ou de difícil manutenção, impedindo a deterioração do sistema.<br />
Nesta mesma obra, [Beck 1999] elaborou três frases de impacto, que servem como<br />
um ponto de partida para entendermos como o TDD afeta a qualidade de um software:<br />
• Toda vez que alguém toma uma decisão e não a testa, existe uma grande probabilidade<br />
de que esta decisão esteja errada;<br />
• Funcionalidades de software que não podem ser demonstradas através de testes<br />
automatizados simplesmente não existem;<br />
• <strong>Testes</strong> nos dão à chance de pensar sobre o que queremos, independente da forma<br />
como a solução será implementada.<br />
Ao utilizar TDD, devemos escrever testes para cada solução implementada. Dessa<br />
forma, diminuímos a probabilidade de tomarmos uma decisão errada. Ao mesmo tempo,<br />
temos a oportunidade de experimentar várias implementações diferentes para o mesmo<br />
problema e escolher aquela mais limpa, elegante e que apresente o melhor desempenho.<br />
Na época em que [Beck 2002] publicou sua obra, afirmou que ainda não haviam<br />
estudos que demonstrassem científicamente as diferenças na qualidade, produtividade ou<br />
diversão entre a utilização de TDD e quaisquer outras alternativas. Atualmente já existem
publicados alguns estudos objetivos sobre o impacto da utilização de TDD com relação à<br />
qualidade e produtividade, frente à maneira tradicional de desenvolvimento.<br />
Em seu blog, [Hawley 2004] publicou os resultados de uma pesquisa que ele<br />
mesmo realizou com a ajuda de seu colega de trabalho. Nesta pesquisa ele constatou<br />
que 92% dos desenvolvedores perceberam que TDD os forçaram a produzir código de<br />
alta qualidade. Constatou também, através dos códigos produzidos pelos participantes,<br />
que houve um incremento na qualidade do código, uma vez que eles tiveram 18% mais de<br />
sucesso nos testes de caixa-preta em comparação com os códigos produzidos da maneira<br />
tradicional.<br />
3.1. Desenho Simplificado e Evolucionário<br />
Escrevendo somente o necessário para os testes e removendo toda a duplicação, você automaticamente<br />
obtém um desenho que é perfeitamente adaptado para os requisitos atuais<br />
e igualmente preparado para todas as futuras funcionalidades [Beck 2002].<br />
Design simplificado reduz os custos porque ao escrever menos código para atender<br />
os requisitos, menos código existirá para ser mantido no futuro. Design simplificado é<br />
mais fácil de se construir, manter e entender.<br />
3.2. Refatoração<br />
Os testes lhe dão a confiança de que grandes refatorações não mudarão o comportamento<br />
do sistema, o que se conclui que, quanto maior a confiança, mais agressivamente você<br />
poderá conduzir refatorações em larga escala que estenderão a vida de seu sistema. A<br />
refatoração torna a elaboração dos próximos testes muito mais fácil [Beck 2002].<br />
Custos são reduzidos porque a refatoração contínua evita que o desenho se degrade<br />
com o passar do tempo, assegurando que o código continue fácil de ser entendido, mantido<br />
e modificado.<br />
3.3. Feedback Constante<br />
[Beck 2002], no último capítulo de sua publicação, afirma que TDD o ajuda a dar atenção<br />
aos problemas certos na hora certa, de forma que o desenho do software fica mais limpo<br />
e com muito menos defeitos. O TDD faz com que o programador ganhe confiança sobre<br />
seu código com o passar do tempo, isto é, à medida que os testes vão se acumulando (e<br />
melhorando), ele ganha confiança no comportamento do sistema. E ainda, à medida que<br />
o desenho é refinado, mais e mais mudanças se tornam possíveis.<br />
Outra vantagem do TDD que [Beck 2002] acredita poder explicar seus efeitos<br />
positivos, é a forma como ele encurta o ciclo de feedback sobre as decisões de desenho.<br />
Ele dura apenas segundos ou minutos, e é seguido pela reexecução dos testes dezenas ou<br />
centenas de vezes por dia. Ao invés de se projetar um desenho e então esperar semanas<br />
ou meses para outra pessoa sentir as dores ou glórias de sua consequência, o feedback<br />
emerge em segundos ou minutos, enquanto você tenta traduzir suas idéias em interfaces<br />
plausíveis.<br />
3.4. Suíte de <strong>Testes</strong> (Regressão)<br />
Usando TDD, os testes unitários são criados num momento onde a funcionalidade a ser<br />
implementada está mais bem definida na mente do programador, e depois podem e devem
ser utilizados para fazer testes de regressão. Uma suíte de testes automáticos feita por<br />
programadores reduz os custos de um software funcionando como uma rede de segurança<br />
de testes que capturam defeitos, problemas de comunicação e ambigüidades antes e permitem<br />
que o desenho possa ser modificado de forma incremental. Esta suíte de testes<br />
gerada pelo TDD é fundamental para viabilizar procedimentos de Integração Contínua 2 .<br />
3.5. Documentação Para Programadores<br />
A suíte de testes serve como uma documentação voltada para o programador que tem um<br />
entendimento mais rápido e facilitado do que uma parte do código faz através do código<br />
que o testa. Cada teste unitário especifica o uso apropriado de uma classe de produção<br />
[Langr 2005].<br />
4. Conclusões<br />
Esta técnica de desenvolvimento produz desenhos menos acoplados que são mais fáceis<br />
de manter, reduz altamente a quantidade de defeitos, e reforça a construção e manutenção<br />
apenas do que é realmente necessário. Finalmente, testes bem escrito atuam como um<br />
tipo de requisitos “executáveis” que ajudam a manter o entendimento compartilhado da<br />
equipe de desenvolvimento, sobre como o sistema de software representa os problemas<br />
do mundo real.<br />
Por outro lado, o fato de se ter um grande número de testes unitários passando<br />
com sucesso, pode passar uma falsa sensação de segurança, resultando na implementação<br />
de menos atividades de garantia de qualidade, como testes de integração e testes de<br />
conformidade.<br />
É importante ressaltar também que, esta técnica não garante a obtenção de níveis<br />
aceitáveis em certos aspectos do software final, como usabilidade e desempenho. Além<br />
disso, TDD não consegue mitigar riscos relacionados com a falta de requisitos ou com<br />
requisitos erroneamente definidos.<br />
Referências<br />
Astels, D. (2003). Test-Driven Development: A Practical Guide. Prentice Hall PTR.<br />
Beck, K. (1999). Extreme Programming Explained: Embrace Change. Addison Wesley.<br />
Beck, K. (2002). Test-Driven Development By Example. Addison Wesley.<br />
Bellware, S. (2009). http://www.tvagile.com/2009/08/13/good-test-better-code-fromunit-testing-to-behavior-driven-development/.<br />
Good Test, Better Code - From Unit<br />
Testing to Behavior-Driven Development (10:40).<br />
Fowler, M. (2006). Continuous integration.<br />
Friedman, L. (2009). Quality - an agile point of view. TE: Testing Experience, Setembro:16<br />
17.<br />
2 Segundo [Fowler 2006], integração contínua é uma pratica de desenvolvimento de software onde os<br />
membros de um time integram seu trabalho frequentemente, geralmente cada pessoa integra pelo menos<br />
diariamente, podendo haver múltiplas integrações por dia. Cada integração é verificada por um build automatizado<br />
(incluindo testes) para detectar erros de integração o mais rápido possível. Muitos times acham<br />
que essa abordagem leva a uma significante redução nos problemas de integração e permite que um time<br />
desenvolva um software coeso mais rapidamente.
Hawley, M. (2004). http://weblogs.asp.net/mhawley/archive/2004/04/15/114005.aspx.<br />
TDD Research Findings.<br />
James P. Womack, Daniel T. Jones, D. R. (1990). The machine that changed the world.<br />
Koskela, L. (2008). Test Driven: Pratical TDD and Acceptance TDD for Java Developers.<br />
Manning.<br />
Langr, J. (2005). Agile Java Crafting Code with Test-Driven Development. Prentice Hall<br />
PTR.<br />
Lewis, W. E. (2004). Software Testing and Continuous Quality Improvement. Auerbach,<br />
2 edition.<br />
Schwaber, K. (2004). Agile Project Management with Scrum. Microsoft Press.<br />
Tian, J. (2005). Software Quality Engineering: Testing, Quality Assurance, and Quantifiable<br />
Improvement. John Wiley & Sons.