Вы находитесь на странице: 1из 8

A Relação Entre Desenvolvimento Orientado a Testes e Qualidade de Software

Cássio L. Ribeiro 1

1 Instituto de Informática – Pontifícia Universidade Católica de Goiás (PUC Goiás) Goiânia – GO – Brasil

cassio.landim@gmail.com

Abstract. This paper shows how the consequences of the use of test-driven de- velopment affect the quality of software. It lists some situations that negatively affect the quality of software, such as the lack of unit testing and presence of disorganized code. It demonstrates how the use of test-driven development miti- gates or eliminates the occurrence of these situations. Finally, it highlights other important quality factors that the test-driven development does not resolve pro- perly.

Resumo. O presente artigo evidencia de que maneira as consequências do em- prego do desenvolvimento orientado a testes afetam a qualidade de um software. São listadas algumas situações que afetam negativamente a qualidade de um software, como a ausência de testes unitários e a presença de código desorga- nizado. Demonstramos como o emprego do desenvolvimento orientado a testes ameniza ou elimina a ocorrência dessas situações. Ao final, evidencia outros fatores importantes para a qualidade, que o desenvolvimento orientado a testes deixa a desejar.

1. Introdução

A natureza ubíqua da computação moderna, software e infraestrutura de informação e, ainda, a demanda crescente por automação e conveniência pela sociedade moderna tem gerado constante aumento de tamanho e complexidade dos sistemas de software moder- nos. Este incremento em tamanho e complexidade acaba por causar consequências não- intencionais em termos de gerar problemas de qualidade [Tian 2005].

A alta complexidade e o amplo escopo dificultam a prevenção ou eliminação de problemas de software e seus impactos negativos relacionados; consequentemente, várias atividades de garantia da qualidade são realizadas para prevenir e/ou eliminar certas clas- ses de problemas, ou ainda, para reduzir a probabilidade ou severidade de tais impactos quando não se pode evitá-los [Tian 2005].

As metodologias ágeis emergiram da necessidade de se minimizar os riscos do desenvolvimento de softwares. Essas metodologias evoluíram como parte de uma rea- ção contrária aos processos de desenvolvimento utilizados na época, caracterizados como “pesados”, burocráticos, micro-gerenciados e com uma alta tendência a se produzir docu- mentos formais [Langr 2005].

Como o desenvolvimento orientado a testes (Test-Driven Development - TDD) é 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, este estudo visa evidenciar de que maneira o emprego do TDD afeta a qualidade de um software.

2. Conceitos de Qualidade de Software e TDD

2.1. Qualidade de Software

[Tian 2005] coloca a qualidade sob a perspectiva das expectativas dos usuários de soft- ware. Em geral, o que as pessoas esperam como qualidade em um sistema de software

é que eles façam o que foram programados para fazer. Em outras palavras, eles devem

fazer a coisa certa, ou seja, eles devem executar suas tarefas específicas corretamente ou satisfatoriamente.

Comportamentos inesperados se manifestando na forma de defeitos, podem afetar profundamente a qualidade de um software, porém, este não é o único fator que deter- mina a qualidade. Segundo [Friedman 2009], as metodologias ágeis tratam o processo de incremento da qualidade como um exercício de “eliminação do desperdício” onde o conceito de “desperdício” assume o mesmo significado no contexto da manufatura lean 1 [James P. Womack 1990], onde um produto de alta qualidade é aquele que previne o desperdício do esforço investido pelo usuário no processo de produzir algo de valor.

[Friedman 2009] identifica três fatores principais de desperdício que reduzem a qualidade de um software. O primeiro e mais comum são os defeitos. Estes podem ser categorizados como ocorrências onde o sistema se comporta de forma inesperada. En- quanto o desenvolvedor espera que o sistema se comporte de certa maneira, o comporta-

mento exibido é diferente devido a defeitos inseridos na fase de codificação. O problema

é que quando um defeito afeta o usuário, em casos menos graves, pode forçar o usuário

a repetir alguma ação, enquanto que em casos extremos pode causar uma perda substan- cial de dados que irá requerer um esforço significativo para serem reproduzidos (quando possível).

O segundo fator está ligado a problemas de usabilidade, onde um produto de di-

fícil uso causa um desperdício de esforço. Em casos extremos, os usuários fazem uso de caminhos alternativos para atingirem seus objetivos; entretanto, em vários casos, os usuários empregam a forma incorreta de execução ao invés de perceber que a qualidade

atual do sistema é insuficiente para atender seus propósitos.

O terceiro fator, funcionalidades do sistema que não são utilizadas, também repre-

sentam um fator de desperdício. Além de inúteis, retardam e dificultam o uso do sistema. Esta é a forma de desperdício que mais despende esforços para ser localizada e eliminada.

2.1.1. Verificação versus Validação

Verificação é provar que o produto atende corretamente os requisitos especificados em atividades prévias durante o ciclo de vida de desenvolvimento, já a validação checa que o

1 O termo lean foi cunhado ao final da década de 80 em um projeto de pesquisa do Massachusetts Institute of Technology (MIT) sobre a indústria automobilística mundial. A pesquisa revelou que a Toyota havia desenvolvido um novo e superior paradigma de gestão nas principais dimensões dos negócios (manufatura, desenvolvimento de produtos e relacionamento com os clientes e fornecedores).

sistema atende os requisitos do cliente ao final do ciclo de vida. Tradicionalmente, teste de software tem sido considerado um processo de validação, isto é, uma fase do ciclo de vida [Lewis 2004].

2.1.2. Qualidade Externa e Interna

Qualidade externa é a qualidade medida pelo cliente, e qualidade interna é a qualidade medida pelas pessoas que possuem acesso ao código, como é o caso dos programadores

e arquitetos.

As metodologias ágeis pregam que a qualidade interna de um software não é ne-

gociável, ela deve ser fixada como ótima durante todo o ciclo de vida do software. Al- gumas equipes, na esperança de reduzir o prazo de entrega, sacrificam temporariamente

a qualidade interna na esperança de que a qualidade externa não sofrerá um grande im-

pacto. Esta estratégia é muito tentadora em um cenário em curto prazo. Porém, eventual- mente problemas de qualidade interna o alcançarão e tornarão aquele software proibitiva- mente caro de manter ou mesmo incapaz de alcançar um nível competitível de qualidade externa [Beck 1999]. Este fenômeno é conhecido como débito tecnológico. Segundo [Friedman 2009], tentar trocar a qualidade por velocidade é uma das estratégias mais er- rôneas existentes.

2.2. Testes Unitários

De acordo com [Lewis 2004], teste unitário é a escala mais “micro” de testes para testar funções particulares ou módulos de códigos. Tipicamente são feitos pelo programador e não por testadores, já que requerem um conhecimento detalhado do código e do design interno do software. Nem sempre são fáceis de se implementar a não ser que a aplicação tenha uma arquitetura muito bem desenhada com código organizado.

[Bellware 2009] acredita que quando testes são escritos após o código de produção

já estar pronto, na realidade o que se está fazendo é um tipo de engenharia reversa, que é

uma forma de engenharia de que exige uma grande quantidade de esforço. Exige muito esforço porque ao se escrever os testes neste momento, o programador precisará ler todo

o código, entender o que ele faz e adivinhar como escrever um teste para este código.

Os testes produzidos desta maneira acabarão, provavelmente, não servindo para cobrir o mínimo necessário dos caminhos alternativos de execução do código.

2.3. Desenvolvimento Orientado a Testes

As metodologias ágeis já se tornaram populares na área de desenvolvimento de software.

A cada dia, mais empresas as adotam para amenizar problemas de qualidade e de entrega

de produtos que agreguem o valor real aos seus clientes.

Algumas metodologias ágeis como Scrum tem foco em práticas gerenciais e or- ganizacionais, enquanto XP tem foco maior em práticas de programação. O TDD é uma das práticas que fazem parte do núcleo do XP [Beck 1999] que entra em cena durante as fases de desenho e codificação de um software.

XP é uma metodologia ágil e leve para equipes de desenvolvimento de software 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, sim- plicidade, feedback, respeito e coragem. Dá ênfase no trabalho em equipe, permitindo que os desenvolvedores respondam com confiança às mudanças de requisitos dos clientes, até mesmo tardiamente no ciclo de vida do projeto.

Scrum é um processo ágil para o desenvolvimento de projetos, sendo mais ade- quado para projetos onde os requisitos estão mudando ou emergindo rapidamente. É mais focado nas pessoas e na forma como elas deverão interagir entre si para atingir um obje- tivo comum.

[Schwaber 2004] afirma que o Scrum baseia todas as suas práticas em uma es- trutura de processo incremental e iterativo. Esta estrutura é formada por iterações de atividades de desenvolvimento que ocorrem uma após a outra, sendo que a saída de cada iteração é um incremento do produto. Dentro desta iteração ocorrem várias iterações mais curtas (inspeções diárias), onde os indivíduos membros do time se encontram para inspe- cionar as atividades dos outros membros e fazerem as adaptações necessárias. Uma lista de requisitos guia a iteração. Este ciclo se repete até que o projeto termine.

Ao início de cada iteração, o time analisa o que deve ser feito. Ele então escolhe o que acredita que possa se tornar um incremento de funcionalidade potencialmente entre- gável ao final da iteração. O time é deixado a sós para fazer seu melhor esforço pelo resto da iteração. Ao final da iteração, o time apresenta o incremento de funcionalidade que construiu para que os patrocinadores do projeto possam inspecioná-lo e fazer ajustes para as próximas iterações do projeto. O processo criativo durante as iterações é o coração da produtividade do Scrum.

Já o TDD é uma prática que visa aumentar a velocidade da entrega de produtos através da simplificação das atividades de desenho de software. [Koskela 2008] resume a filosofia do TDD em uma frase – somente escreva código para fazer um teste falho passar. [Astels 2003] define o TDD como sendo um estilo de desenvolvimento onde:

Uma suíte exaustiva de testes de programadores é mantida;

Nenhum código entra em produção a não ser que tenha testes associados;

Os testes são escritos antes;

Os testes determinam que código precise ser escrito.

2.3.1. Ciclo Teste-Codificação-Refatoração

TDD é uma técnica de desenho e desenvolvimento que nos ajuda a construir um sistema de forma incremental, com a consciência de que nunca nos afastamos de uma baseline funcional e entregável. O ciclo Teste-Codificação-Refatoração é quem dita o ritmo do desenvolvimento através de pequenos e controlados passos. A sequência clássica que nos foi ensinada, onde primeiramente produzimos o desenho, implementamos o desenho, e só então testamos o software em busca dos defeitos, é totalmente invertida quando com- parada ao ciclo Teste-Codificação-Refatoração.

Primeiramente escrevemos o teste, depois escrevemos o código para fazer este teste passar e então passamos para a fase de desenho. Esta fase de desenho é diferente da fase de desenho tradicional. No TDD ela é chamada de refatoração, onde o desenho atual do código é transformado em um desenho melhor.

O ciclo Teste-Codificação-Refatoração também pode ser chamado de ciclo

Vermelho-Verde-Refatoração [Astels 2003]. O vermelho simboliza a fase inicial do ci- clo TDD onde o teste escrito falha. Ele falha porque o sistema não está funcional; ele

não possui as funcionalidades que desejamos que ele tenha. Em alguns ambientes de desenvolvimento, essa falha é evidenciada através da exibição de uma barra vermelha.

Na segunda fase implementamos as funcionalidades que faltavam para fazer todos

os testes passarem, ou seja, os testes que já existiam e o novo teste recém-introduzido. Neste momento, a barra visual deve se tornar verde. Somente se passa para a próxima etapa quando nenhum teste estiver falho.

Na última parte do ciclo é feita a refatoração. Refinamos o desenho do código sem alterarmos seu comportamento externo, mantendo todos os testes passando e a com a barra visual exibindo a cor verde.

3. A Relação entre TDD e Qualidade

Qualidade não pode ser alcançada através da avaliação de um produto já feito. O objetivo, portanto, é prevenir defeitos de qualidade ou deficiências em primeiro lugar, tornando os produtos avaliáveis através de medidas de garantia de qualidade [Lewis 2004].

[Beck 1999] cita alguns exemplos de riscos relacionados ao desenvolvimento de software. Dos riscos citados, dois estão diretamente ligados a qualidade de software e podem ser tratados através da utilização de TDD:

Taxa de defeitos – o software é colocado em produção mas a taxa de defeitos é tão alta que ele acaba não sendo utilizado. TDD eleva a validação de um software a um patamar superior, testando-o função por função;

Deterioração do sistema – o software é colocado em produção com sucesso, porém após algum tempo o custo de se fazer modificações ou a taxa de defeitos aumenta tanto que o sistema precisa ser substituído. TDD mantém o programador focado

na solução, de forma que o software não fica carregado de códigos desnecessários, duplicados ou de difícil manutenção, impedindo a deterioração do sistema.

Nesta mesma obra, [Beck 1999] elaborou três frases de impacto, que servem como um ponto de partida para entendermos como o TDD afeta a qualidade de um software:

Toda vez que alguém toma uma decisão e não a testa, existe uma grande probabi- lidade de que esta decisão esteja errada;

Funcionalidades de software que não podem ser demonstradas através de testes automatizados simplesmente não existem;

Testes nos dão à chance de pensar sobre o que queremos, independente da forma como a solução será implementada.

Ao utilizar TDD, devemos escrever testes para cada solução implementada. Dessa

forma, diminuímos a probabilidade de tomarmos uma decisão errada. Ao mesmo tempo, temos a oportunidade de experimentar várias implementações diferentes para o mesmo problema e escolher aquela mais limpa, elegante e que apresente o melhor desempenho.

Na época em que [Beck 2002] publicou sua obra, afirmou que ainda não haviam

estudos que demonstrassem científicamente as diferenças na qualidade, produtividade ou 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 à qualidade e produtividade, frente à maneira tradicional de desenvolvimento.

Em seu blog, [Hawley 2004] publicou os resultados de uma pesquisa que ele mesmo realizou com a ajuda de seu colega de trabalho. Nesta pesquisa ele constatou que 92% dos desenvolvedores perceberam que TDD os forçaram a produzir código de alta qualidade. Constatou também, através dos códigos produzidos pelos participantes, que houve um incremento na qualidade do código, uma vez que eles tiveram 18% mais de sucesso nos testes de caixa-preta em comparação com os códigos produzidos da maneira tradicional.

3.1. Desenho Simplificado e Evolucionário

Escrevendo somente o necessário para os testes e removendo toda a duplicação, você au- tomaticamente obtém um desenho que é perfeitamente adaptado para os requisitos atuais

e igualmente preparado para todas as futuras funcionalidades [Beck 2002].

Design simplificado reduz os custos porque ao escrever menos código para atender os requisitos, menos código existirá para ser mantido no futuro. Design simplificado é mais fácil de se construir, manter e entender.

3.2. Refatoração

Os testes lhe dão a confiança de que grandes refatorações não mudarão o comportamento do sistema, o que se conclui que, quanto maior a confiança, mais agressivamente você poderá conduzir refatorações em larga escala que estenderão a vida de seu sistema. A refatoração torna a elaboração dos próximos testes muito mais fácil [Beck 2002].

Custos são reduzidos porque a refatoração contínua evita que o desenho se degrade

com o passar do tempo, assegurando que o código continue fácil de ser entendido, mantido

e modificado.

3.3. Feedback Constante

[Beck 2002], no último capítulo de sua publicação, afirma que TDD o ajuda a dar atenção aos problemas certos na hora certa, de forma que o desenho do software fica mais limpo

e com muito menos defeitos. O TDD faz com que o programador ganhe confiança sobre

seu código com o passar do tempo, isto é, à medida que os testes vão se acumulando (e

melhorando), ele ganha confiança no comportamento do sistema. E ainda, à medida que

o desenho é refinado, mais e mais mudanças se tornam possíveis.

Outra vantagem do TDD que [Beck 2002] acredita poder explicar seus efeitos positivos, é a forma como ele encurta o ciclo de feedback sobre as decisões de desenho. Ele dura apenas segundos ou minutos, e é seguido pela reexecução dos testes dezenas ou centenas de vezes por dia. Ao invés de se projetar um desenho e então esperar semanas ou meses para outra pessoa sentir as dores ou glórias de sua consequência, o feedback emerge em segundos ou minutos, enquanto você tenta traduzir suas idéias em interfaces plausíveis.

3.4. Suíte de Testes (Regressão)

Usando TDD, os testes unitários são criados num momento onde a funcionalidade a ser 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 programadores reduz os custos de um software funcionando como uma rede de segurança de testes que capturam defeitos, problemas de comunicação e ambigüidades antes e per- mitem que o desenho possa ser modificado de forma incremental. Esta suíte de testes gerada pelo TDD é fundamental para viabilizar procedimentos de Integração Contínua 2 .

3.5. Documentação Para Programadores

A suíte de testes serve como uma documentação voltada para o programador que tem um entendimento mais rápido e facilitado do que uma parte do código faz através do código que o testa. Cada teste unitário especifica o uso apropriado de uma classe de produção [Langr 2005].

4. Conclusões

Esta técnica de desenvolvimento produz desenhos menos acoplados que são mais fáceis de manter, reduz altamente a quantidade de defeitos, e reforça a construção e manutenção apenas do que é realmente necessário. Finalmente, testes bem escrito atuam como um tipo de requisitos “executáveis” que ajudam a manter o entendimento compartilhado da equipe de desenvolvimento, sobre como o sistema de software representa os problemas do mundo real.

Por outro lado, o fato de se ter um grande número de testes unitários passando com sucesso, pode passar uma falsa sensação de segurança, resultando na implementa- ção de menos atividades de garantia de qualidade, como testes de integração e testes de conformidade.

É importante ressaltar também que, esta técnica não garante a obtenção de níveis aceitáveis em certos aspectos do software final, como usabilidade e desempenho. Além disso, TDD não consegue mitigar riscos relacionados com a falta de requisitos ou com requisitos erroneamente definidos.

Referências

Astels, D. (2003). Test-Driven Development: A Practical Guide. Prentice Hall PTR.

Beck, K. (1999). Extreme Programming Explained: Embrace Change. Addison Wesley.

Beck, K. (2002). Test-Driven Development By Example. Addison Wesley.

Bellware, S. (2009). http://www.tvagile.com/2009/08/13/good-test-better-code-from- unit-testing-to-behavior-driven-development/. Good Test, Better Code - From Unit Testing to Behavior-Driven Development (10:40).

Fowler, M. (2006). Continuous integration.

Friedman, L. (2009). Quality - an agile point of view. TE: Testing Experience, Setem- bro:16 17.

2 Segundo [Fowler 2006], integração contínua é uma pratica de desenvolvimento de software onde os membros de um time integram seu trabalho frequentemente, geralmente cada pessoa integra pelo menos diariamente, podendo haver múltiplas integrações por dia. Cada integração é verificada por um build auto- matizado (incluindo testes) para detectar erros de integração o mais rápido possível. Muitos times acham que essa abordagem leva a uma significante redução nos problemas de integração e permite que um time desenvolva um software coeso mais rapidamente.

Hawley, M. (2004).

http://weblogs.asp.net/mhawley/archive/2004/04/15/114005.aspx.

TDD Research Findings.

James P. Womack, Daniel T. Jones, D. R. (1990). The machine that changed the world.

Koskela, L. (2008). Test Driven: Pratical TDD and Acceptance TDD for Java Developers. Manning.

Langr, J. (2005). Agile Java Crafting Code with Test-Driven Development. Prentice Hall PTR.

Lewis, W. E. (2004). Software Testing and Continuous Quality Improvement. Auerbach, 2 edition.

Schwaber, K. (2004). Agile Project Management with Scrum. Microsoft Press.

Tian, J. (2005). Software Quality Engineering: Testing, Quality Assurance, and Quantifi- able Improvement. John Wiley & Sons.