81,9(56,'$'()('(5$/'25,2*5$1'('268/
,167,7872'(,1)250È7,&$
7HVW'ULYHQ'HYHORSPHQW
Otávio Gaspareto
REJDVSDUHWR#LQIXIUJVEU
,QWURGXomR
7HVWGULYHQGHYHORSPHQW, ou TDD, também conhecido por WHVWILUVWGHYHORSPHQW, é
um conjunto de técnicas associadas com ([WUHPH 3URJUDPPLQJ(XP) e métodos ágeis.
TDD, no processo XP, é a única maneira de se codificar [1].
Segundo Miller [12], TDD reivindica um desenvolvimento incremental do código
que inicia com testes, incluindo freqüentemente testes de regressão. Uma das regras do
TDD é a seguinte: “se você não consegue escrever um teste para aquilo que você está
codificando, então você nem deveria pensar sobre codificar”. Esta técnica de
desenvolvimento é antiga, mas tornou-se conhecida, após sua adoção em metodologias
ágeis de desenvolvimento, tornando assim, um desenvolvimento incremental, onde o
código é escrito para passar pelos testes já escritos previamente. Quando uma nova
funcionalidade é adicionada ao sistema, isto deve manter a estrutura simples inicial intacta,
e pode ser que seja necessário reestruturar algo. Isto, entretanto, pode quebrar alguma outra
funcionalidade que já estivesse funcionado. Para garantir que isto não irá acontecer, testes
automatizados devem acontecer [6].
Atualmente, TDD tem sido usado em muitas áreas de tecnologia, além do
desenvolvimento de software, como também no desenvolvimento de sistemas embarcados
[5], onde além do software, é desenvolvido o hardware sob o qual ele irá ser executado.
Em algumas universidades, TDD está sendo ensinado junto com as cadeiras básicas de
programação, a fim de enfatizar testes como sendo uma prática fundamental para a carreira
de informata [13] [15].
Este artigo pretende realizar uma revisão mostrando do que trata a TDD e o que
existe de atual nesta disciplina, como técnicas associadas, SDWWHUQV e ferramentas
disponíveis.
2TXHp7HVW'ULYHQ'HYHORSPHQW
De acordo com Kent Beck [18], um método ágil é comparável ao ato de dirigir um
carro: você deve observar a estrada e fazer correções contínuas para se manter no caminho.
Neste contexto onde a agilidade é fundamental, o testador seria aquele que ajuda o
motorista a chegar com segurança ao seu destino, impedindo que sejam feitas conversões
incorretas durante o percurso, evitando que o motorista se perca e fazendo com que ele pare
e peça instruções quando necessário. Neste ambiente, destaca-se o TDD, como sendo uma
abordagem evolutiva na qual o desenvolvedor escreve o teste antes de escrever o código
funcional necessário para satisfazer aquele teste [6].
Segundo Marrero [15], o objetivo principal do TDD é especificação e não
validação. Em outras palavras, é uma forma de refletir sobre a modelagem antes de escrever
código funcional. Já segundo Baumeister [6], desenvolvimento dirigido por testes é uma
técnica de programação onde o principal objetivo é escrever código funcional limpo a partir
de um teste que tenha falhado. Como efeito colateral, obtém-se um código fonte bem
testado.
Após escrever os casos de teste, que devem especificar apenas uma pequena parte
da funcionalidade, inicialmente nem devem compilar, pois a funcionalidade ainda nem foi
escrita, o desenvolvedor deve implementar o código necessário apenas para passar pelo
teste. Feito isto, fica a cargo do desenvolvedor realizar o UHIDFWRU do código, garantindo que
continue a forma mais simples de código para satisfazer a determinada funcionalidade [2].
O ciclo, de uma visão macro, poderia ser visto como: escrever poucos casos de teste,
implementar o código; escrever poucos casos de teste, implementar o código, e assim por
diante. A figura 1 ilustra como é feito o processo de TDD.
)LJXUD±&LFORGR7''
O processo XP sugere, que outras disciplinas sejam utilizadas junto com o TDD, como por
exemplo, a SDLUSURJUDPPLQJ (programação em par) e o UHIDFWRULQJ.
Existem várias questões que são levantadas da adoção do TDD em um projeto. Uma
delas é em relação ao que não deve ser testado. Kent Beck [2] diz que devem ser testados
condições, ORRSV, operações e polimorfismos, porém, somente aqueles que o próprio
desenvolvedor escreveu.
Quanto à questão sobre se os testes estão bons, há três pontos que devem ser observados:
i)
se são necessárias muitas linhas de código criando objetos para uma simples
asserção, então há algo de errado;
ii)
se não é possível encontrar facilmente um lugar comum para o código de
inicialização, então existem muitos objetos firmemente associados.
iii)
testes que quebram inesperadamente sugerem que uma parte da aplicação está
afetando outra parte. É necessário projetar até que esta distância seja eliminada,
ou quebrando esta conexão ou trazendo as duas partes juntas.
Uma vantagem significativa do TDD é que ele possibilita ao desenvolvedor dar
pequenos e controlados passos durante a codificação do software [6]. Esta prática é mais
aconselhável do que escrever grandes quantidades de código de uma só vez.
Alguns motivos para a adoção do TDD, segundo Kaufmann [10], são:
i)
o desenvolvimento parte do princípio dos objetivos, após isto, é pensado nas
possíveis soluções;
ii)
o entendimento do sistema pode ser feito a partir da leitura dos testes;
iii)
não é desenvolvido código desnecessário;
iv)
não existe código sem teste;
v)
uma vez um teste funcionando, é sabido que ele irá funcionar sempre, servindo
também, como teste de regressão;
vi)
os testes permitem que seja feito UHIDFWRU, pois eles garantirão que as mudanças
não alteram o funcionamento do sistema.
Segundo Mugridge [7], como conseqüências do uso do TDD, têm-se que o código é
escrito de maneira testável isoladamente, pois geralmente, códigos escritos da maneira
tradicional, possuem alto acoplamento e uma pobre orientação a objetos. Os testes atuam
como uma documentação do sistema, pois eles seriam os primeiros clientes a usarem as
classes desenvolvidas, mostrando ao desenvolvedor, o que é necessário fazer.
Outros benefícios provindos da adoção do modelo de desenvolvimento TDD foram
abordados por Müller [1], no qual, foi feita uma análise do retorno de investimento em se
utilizar TDD ao invés de modelos convencionais de desenvolvimento. Nele, obteve-se a
qualidade final do código, era superior ao modelo convencional, pois não seria necessário
perder uma grande fatia de tempo no final do projeto para a correção de defeitos, pois estes
eram capturados e corrigidos ao longo do desenvolvimento do sistema. A figura 2 mostra
de forma clara a diferença existente entre os dois modelos.
)LJXUD±&RPSDUDomR7''[0RGHORFRQYHQFLRQDOGHGHVHQYROYLPHQWR>@
7''HWHVWHVWUDGLFLRQDLV
TDD é primariamente uma técnica de programação que garante que o código de um
sistema esteja inteiramente testado de forma unitária [20]. Entretanto, há mais teste do que
isto, pois ainda é necessário que sejam realizados os tradicionais testes, como testes
funcionais, testes de usuário, testes de integração, entre outros. A maioria destes testes
podem ser realizados tardiamente ao sistema [20].
Com a realização de testes tradicionais, serão encontrados mais defeitos. Isto ocorre
com o TDD; quando um teste falha, o desenvolvedor sabe que necessita resolver o
problema [20]. TDD aumenta a credibilidade de que o sistema realmente funciona, e está de
acordo com os requerimentos propostos [2].
Um interessante efeito do TDD, é que é possível cobrir 100% do sistema com testes,
pois cada linha de código é testada, sendo algo que não é garantido com testes tradicionais.
Estudos comprovam que sistemas desenvolvidos utilizando-se de TDD resultam com
melhor código e menos defeitos do que sistemas que utilizaram somente testes tradicionais
[9] [13].
)HUUDPHQWDV
Existem atualmente diversas ferramentas, tanto RSHQVRXUFH como proprietárias, que
apóiam o desenvolvimento de sistemas baseados em TDD, dentre elas, podemos citar
IUDPHZRUNV para testes unitários, os chamados xUnits, entre os quais, o mais famoso é o
JUnit, e para a criação do PRFNobjects, como também editores de código que auxiliam no
desenvolvimento tanto do código como na execução dos testes.
-8QLW
JUnit é um IUDPHZRUN implementado inteiramente em Java, seguindo a filosofia do
código-livre, que possibilita a criação de suítes de testes [19]. Uma suíte de testes, nada
mais é, do que uma grande coleção de classes de testes, onde cada conjunto de classes é
responsável por testar uma classe ou uma pequena funcionalidade do código que será
colocado em produção [16].
Outra facilidade que a ferramenta apresenta é uma interface gráfica que facilita a
execução da suíte de teste, e conta também, com uma barra de progresso, mostrando ao
usuário o sucesso ou falha dos métodos de testes individualmente, assim que eles são
executados. A figura 3 apresenta um teste que foi executado com sucesso. Nota-se que a
barra verde está relacionado com o lema do JUnit, que é “NHHSWKHEDUJUHHQWRNHHSWKH
FRGHFOHDQ” [19].
)LJXUD±6XFHVVRQDH[HFXomRGHWHVWH>@
Quando algum teste falhar, a barra troca de cor, e passa de verde para vermelha, alertando
assim o desenvolvedor que algo está errado. Esta interface gráfica, também exibe a causa
do erro, e mostra informações completas sobre a execução da suíte de testes, como é
exibido na figura 4.
)LJXUD±)DOKDQDH[HFXomRGHWHVWH>@
Com JUnit, um test case é implementado como sendo uma subclasse de
junit.framework.TestCase. Para realizar o teste de comparação entres os valores
esperados com os valores obtidos, são utilizados métodos como assertEquals,
assertTrue, assertNull, entre outros [14]. O método setUp() é invocado para inicializar
a suíte de testes antes da execução dos mesmos, enquanto que o método tearDown() é
usado após finalizar a suíte de testes.
Os métodos que irão realizar os testes no sistema devem iniciar com a palavra “test”, pois é
utilizada a reflexão do Java para a chamada dos mesmos; não devem retornar nenhum
valor, ou seja, serem métodos void; e não devem receber nenhum parâmetro.
0RFN2EMHFWV
0RFN2EMHFWV são utilizados com o propósito de escrever testes como se tivéssemos
tudo o que realmente necessitamos do ambiente [11]. Este processo apresenta ao
desenvolvedor o que o ambiente deve prover para o desenvolvimento do sistema.
Ao se testar partes isoladas do sistema, como apenas um objeto, por exemplo, o
programador deve levar em consideração a interação deste objeto em questão com outros
objetos. A figura 5 apresenta como é realizado um teste usando-se PRFNREMHWFV. No caso,
têm-se um objeto A, que necessita interar-se com um serviço que é disponibilizado pelo
objeto S, mas como ainda não se tem uma implementação de S, ou este será implementado
por terceiros, realiza-se o PRFN do objeto S, forçando assim, o uso de interfaces para o
desenvolvimento.
)LJXUD±8VRGHPRFNREMHFWV>@
Um conhecido IUDPHZRUN utilizado para o desenvolvimento de PRFN REMHFWV é o
jMock[11], que assim como o JUnit, é escrito em Java. O ponto de entrada principal do
jMock, é a classe MockObjectTestCase, que faz uso da classe TestCase do JUnit.
Os PRFN objetos são criados a partir do método mock(...), o qual recebe como parâmetro
um objeto Class representando um tipo de interface, e retorna um objeto Mock, que
imlpementa este interface.
3DWWHUQVSDUD7''
Existem inúmeros padrões para várias atividades na área de informática, entre eles,
os mais conhecidos são os GHVLJQ SDWWHUQV, que são utilizados na construção de sistemas.
Para a disciplina de TDD não é diferente, e existem alguns padrões, que no âmbito geral,
tornam-se mais dicas sobre o que testar em um sistema.
7HVWHVLVRODGRV
A execução de um teste não pode afetar outro teste. Segundo Beck [2], os testes
devem ser desenvolvidos de maneira que sejam rápidos de serem executados, assim, eles
podem ser rodados a todo o momento, não necessitando de um momento específico de
execução de testes. Dessa forma, os testes isolados servem para que uma execução de um
determinado teste, não interfira na execução do teste seguinte, não necessitando assim,
iniciar e parar o sistema assim que cada teste é executado.
/LVWDGHWHVWHV
De acordo com Beck [2], antes de iniciar a codificação, devem ser feita uma lista
com todos os testes que o desenvolvedor acredita que será necessário escrever. Assim,
quando o desenvolvedor sentar para começar a programar, ele terá um guia do que é
necessário fazer, e não corre o risco de esquecer algo.
7HVWHSULPHLUR
Os testes devem ser escritos antes do código que será testado [2]. Os testes servem
para dar um IHHGEDFN rápido do desenvolvimento do sistema. Assim que o tempo passa,
mais estressado fica o desenvolvedor, e, portanto, caso os testes sejam deixados para o
final, o desenvolvedor irá aumentar o nível de estresse, diminuindo a quantidade de testes
no sistema, e impactando na qualidade do produto final.
'DGRVGHWHVWH
Segundo Beck [2], devem ser utilizados dados que tornem os testes fáceis de ler e
seguir. Caso o sistema tenha múltiplas entradas, então, devem existir testes que abranjam
estas entradas. Porém, é desnecessário ter uma lista com dez entradas, se com apenas três,
cobrem-se todas as alternativas [2].
'DGRVHYLGHQWHV
Devem ser incluídos nos testes os dados esperados e o resultado atual, e tentar exibir
um relacionamento aparente entre eles, pois os testes não são escritos apenas para o
computador, e sim, para que futuros desenvolvedore possam entender o código como sendo
uma parte da documentação do sistema [2].
'LVFLSOLQDVUHODFLRQDGDVGR;3
Mesmo TDD sendo uma disciplina do XP [18], esta não se encontra isolada no
universo do ([WUHPH 3URJUDPPLQJ. Abaixo, uma lista de disciplinas que são
complementares e/ou completadas usando-se o TDD [2]:
•
3URJUDPDomR HP SDUHV – os testes escritos no TDD são excelentes peças de
conversação quando se está trabalhando de forma pareada. A programação pareada
melhora o TDD no momento em que o programador que está codificando o código
está cansado, assim, o outro pra ficaria se encarregaria de codificar;
•
,QWHJUDomR FRQWtQXD – testes são um excelente recurso para esta disciplina,
permitindo que sempre seja feita uma integração. Os ciclos são definidos em
períodos curtos, algo entre quinze e trinta minutos, ao invés de uma ou duas horas
de codificação;
•
'HVLJQ VLPSOHV – codificando apenas o necessário para os testes, e removendo
código duplicado, automaticamente obtém-se um design adaptado para a
necessidade atual.
•
5HIDFWRULQJ – a regra de remoção de código duplicado é outra maneira de se fazer
UHIDFWRULQJ. Mas os testes conferem ao desenvolvedor, que grandes mudanças feitas
no sistema, não alteraram o funcionamento do mesmo. Quanto mais seguro de que
mudanças feitas não alteram o sistema, mais agressivo se torna o UHIDFWRULQJ
aplicado pelo desenvolvedor.
•
(QWUHJD FRQWtQXD – com testes realizados no sistema, têm-se mais confiança na
entrega de partes do sistema, e o código vai para produção mais rápido.
&RQVLGHUDo}HVILQDLV
Diante das possibilidades existentes atualmente para o desenvolvimento de
sistemas, nota-se que TDD torna-se uma alternativa viável para as organizações utilizarem
em seus processos de desenvolvimento iterativo.
5HIHUrQFLDV
[1] M. Müller, F. Padberg, About the Return on Investment of Test-Driven
Development.
Disponível
em
http://www.ipd.uka.de/mitarbeiter/
muellerm/publications/edser03.pdf.
[2] K. Beck, Test-Driven Development By Example. Addison Wesley, 2000.
Estados Unidos
[3] L. Crispin, T. House, The Need for Speed: Automatic Acceptance Testing in an
Extreme Programming Environment. Tensegrent, EUA
[4] H. Heinecke, C. Noak, Integrating Extreme Programming and Contracts.
Disponível em http:// www.heinecke.com/publications/XPandContracts.pdf
[5] J. Grenning, Progress before hardware. Disponível em
http://www.objectmentor.com/resources/articles/ObjectMentor/resources/articles/Pr
ogressBeforeHardware
[6] H. Baumeister, M. Wirsing, Applying Test-First Programming and Iterative
Development in Building an E-Business Application. Disponível em http://
www.pst.informatik.uni-muenchen.de/ projekte/caruso/ssgrr2002w.pdf
[7] R. Mugridge, Test Driven Development and the Scientific Method. Disponivel
em http://www.agiledevelopmentconference.com/ 2003/files/P6Paper.pd
[8] D. Saff, M. Ernst, An Experimental Evaluation of Continuous Testing During
Development. Disponivel em http://pag.csail.mit.edu/~mernst/ pubs/ct-user-studyissta2004.pdf
[9] B. George, L. Williams, An Initial Investigation of Test Driven Development In
Industry.
Disponivel
em
http://collaboration.csc.ncsu.edu/laurie/Papers/TDDpaperv8.pdf
[10] R. Kaufmann, D. Janzen, Implications of Test-Driven Development A Pilot
Study.
Disponivel
em
http://people.bethelks.edu/djanzen/Papers/pos12kaufmann.pdf
[11] S. Freeman, T. Mackinnon, N. Pryce, Mock Roles, Not Objects. Disponivel em
http://www.jmock.org/oopsla2004.pdf
[12] K. W. Miller, Test Driven Development on the Cheap: Text Files and Explicit
Scaffolding.
Disponivel
em
http://www.ccsc.org/northwest/docarchive/
2004/augustmailing2004final.doc
[13] C. G. Jones, Test-Driven Development Goes to School. Disponível em
http://portal.acm.org/citation.cfm?id=1040261&
dl=ACM&coll=GUIDE&CFID=15151515&CFTOKEN=6184618
[14] M. Olan, Unit Testing: Test Early, Test Often. Disponivel em http://
www.citeulike.org/user/tinkha/article/126490
[15] W. Marrero, A. Settle, Testing First: Emphasizing Testing in Early
Programming
Courses.
Disponivel
em
http://portal.acm.org/ft_gateway.cfm?id=1067451&type=pdf&coll=GUIDE&dl=G
UIDE&CFID=69236573&CFTOKEN=4183298
[16] J. Langr, Evolution of Test and Code Via Test-First Design. Disponivel em
http://www.objectmentor.com/resources/articles/tfd.pdf
[17]
Wikipedia.
driven_development
Disponível
em
http://en.wikipedia.org/wiki/Test-
[18] K. Beck, Extreme Programming Explained. Addison Wesley, 2000. Estados
Unidos
[19] JUnit. Disponível em: http://www.junit.org
[20] Agile Data. Disponível em http://www.agiledata.org/essays/tdd.html
Download

Test Driven Development