Você está na página 1de 60

Test Driven Development

Unochapec

Cezar Junior de Souza


E-mail: cezar08@unochapeco.edu.br

Sumrio

Introduo

O que ?

Ciclo

Por que devemos testar?

Por que no testamos?

Testes automatizados

Concluso

Teste de unidade

Primeiro teste de unidade

Sumrio

PHPUnit

O que ?

Objetivos

Instalao

Asseres

assertEquals
assertFalse
assertInstanceOf
assertCount
assertEmpty
assertNull
assertTrue

Sumrio

Exemplo prtico

O problema dos nmeros romanos

Refletindo sobre o assunto


Anlise e cobertura de cdigo

Brainstorming

Referncias

Introduo

O que TDD?

uma das prticas de desenvolvimento de software


sugeridas por diversas metodologias.

Prega a ideia de fazer com que o desenvolvedor


escreva testes automatizados de maneira constante
ao longo do desenvolvimento.

Sugere que o desenvolvedor escreva o teste antes


mesmo da implementao.

Introduo

O que TDD?

desenvolvido organicamente, com o feedback do


cdigo executvel exibido entre as decises.

O desenvolvedor escreve os prprios testes porque


no pode esperar 20 vezes por dia por algum para
escrev-los.

Utilizando a tcnica as baterias de testes tendem a


ser maiores, cobrindo mais casos, e garantindo uma
maior qualidade externa

Introduo

O que o TDD?

A prtica nos ajuda a escrever um software melhor,


com mais qualidade, e um cdigo melhor, mais fcil
de ser mantido e evoludo.

Introduo

Toda prtica que ajuda a aumentar a qualidade


do software produzido deve ser
estudada.(Aniche, 2012)

Ciclo
Escrever o teste->Teste Falha->Escreve o programa->Teste passa->Refatora

Por que devemos testar?

necessria somente uma resposta para esta pergunta,


para ter a certeza que o nosso cdigo faz o que deve
fazer.
A quantidade de software que no funciona incrvel.

Por que devemos testar?

Os Estados Unidos estimam que bugs de software lhes


custam aproximadamente 60 bilhes de dlares por
ano...

Fonte: Computer World. Study: Buggy software costs users, vendors nearly 60b annually. http://www.computerworld.com/s/article/72245/Study_Buggy_software_
costs_users_vendors_nearly_60B_annually.

Por que devemos testar?

Um erro de software pode matar pessoas

o foguete Ariane 5 explodiu por um erro de software;

um hospital panamenho matou pacientes pois seu


software para dosagem de remdios errou.

Por que no testamos?

No h um desenvolvedor que no saiba que a soluo


para o problema testar seus cdigos.
No testamos, porque testar sai caro.
Testar sai caro porque estamos pagando a pessoa
errada para fazer o trabalho.

Por que no testamos?


interessante a quantidade de tempo que gastamos
criando solues tecnolgicas para resolver problemas
dos outros.
Por que no escrevemos programas que resolvam
tambm os nossos problemas?

Testes automatizados

Uma maneira para conseguir testar o sistema todo de


maneira constante e contnua a um preo justo
automatizando os testes.
O teste automatizado executaria muito rpido;
Se ele executa
constantemente;

rpido,

logo

rodaramos

Se os rodarmos o tempo todo, descobriramos os


problemas mais cedo, diminuindo o custo que o bug
geraria.

Testes automatizados

Mas a equipe de desenvolvimento no gastar tempo


escrevendo cdigo de teste?
Antes ela s gastava tempo com cdigo de produo,
essa equipe ficar menos produtiva?

Testes automatizados

A resposta para essa pergunta :

O que produtividade?

Se produtividade for medida atravs do nmero de


linhas de cdigo de produo escritos por dia, talvez o
desenvolvedor seja sim menos produtivo, mas, se
produtividade for a quantidade de linhas de cdigo de
produo sem defeitos escritos por dia, o
desenvolvedor ser mais produtivo ao utilizar testes
automatizados.

Concluso

Um mdico, ao longo de uma cirurgia, nunca abre mo de


qualidade. Se o paciente falar para ele: Doutor, o senhor poderia
no lavar a mo e terminar a cirurgia 30 minutos mais cedo?,
tenho certeza que o mdico negaria na hora. Ele saberia que
chegaria ao resultado final mais rpido, mas a chance de um
problema to grande, que simplesmente no valeria a pena.

Concluso

Em nossa rea, justamente o contrrio.


Qual desenvolvedor nunca escreveu um cdigo de m
qualidade de maneira consciente?
Quem nunca escreveu uma gambiarra"?
Quem nunca colocou software em produo sem
executar o mnimo suficiente de testes para tal?

Concluso

No h desculpas para no testar software.

A soluo para que seus testes sejam sustentveis automatizando;

Testar divertido, aumenta a qualidade do seu produto, e pode ainda ajud-lo a identificar
trechos de cdigo que foram mal escritos ou projetados;
Te livram vrias vezes da chatice do seu inimigo natural, o testador;

Testador
Programadores

Enfim, muita vantagem.

Teste de unidade

Desenvolvedores, quando pensam em teste de software,


geralmente imaginam um teste que cobre o sistema
como um todo.
Um teste de unidade no se preocupa com todo o
sistema; ele est interessado apenas em saber se uma
pequena parte do sistema funciona.
Um teste de unidade testa uma nica unidade do nosso
sistema. Geralmente, em sistemas orientados a objetos,
essa unidade a classe.

Teste de unidade

A ideia termos baterias de testes de unidade


separadas para cada uma das classes do sistema;
Cada bateria preocupada apenas com a sua classe.

Teste de unidade

Desenvolvedores gastam toda sua vida automatizando


processos de outras reas de negcio, criando sistemas para
RHs, controle de caixa, entre outros, com o intuito de facilitar a
vida daqueles profissionais.

Por que no criar software que automatize o seu


prprio ciclo de trabalho?

Testes
automatizados
so
fundamentais
para
um
desenvolvimento de qualidade, sua existncia traz diversos
benefcios, como aumento da qualidade e a diminuio de
bugs em produo.

Primeiro teste de unidade

Neste primeiro teste vamos de um simples cdigo


baseado em echo e vamos at um teste totalmente
automatizado;
Imagine que temos que testar um vetor do PHP, uma
pequena funcionalidade a se testar a funo count();
Para um vetor recm criado esperamos que a funo
count retorne 0;
Aps adicionarmos um elemento, count dever retornar
1;

Primeiro teste de unidade

Testando o vetor parte 1:


<?php
<?php
$componente
$componente==array();
array();
////espera-se
que
$componente
espera-se que $componenteesteja
estejavazio.
vazio.
$componente[]
$componente[]=='elemento';
'elemento';
////espera-se
espera-seque
que$componente
$componentecontenha
contenhaum
umelemento.
elemento.
?>
?>

Primeiro teste de unidade

Testando o vetor parte 2:

Um jeito bem simples te testar que estamos obtendo os resultados


que esperamos imprimir o resultado antes e depois de adicionarmos
o elemento. Se obtivermos 0 e depois 1, a funo count se comporta
como o esperado.

<?php
<?php
$componente
$componente==array();
array();
echo
echocount($componente).
count($componente).\n;
\n;
$componente[]
$componente[]=='elemento';
'elemento';
echo
echocount($componente).
count($componente).\n;
\n;
//Sadas:
//Sadas:
////00
//1
//1
?>
?>

Primeiro teste de unidade

Testando o vetor parte 3

Vamos mudar de testes que exigem interpretao manual para testes


que podem executar automaticamente. Escrevemos a comparao do
valor esperado e do real em nosso cdigo de teste e imprimimos ok se
os valores forem iguais. Se alguma vez virmos uma mensagem no ok
saberemos que algo est errado.

<?php
<?php
$componente
$componente==array();
array();
echo
echocount($componente)
count($componente)==
==00?? ok
ok\n
\n: :no
nook
ok\n;
\n;
$componente[]
$componente[]=='elemento';
'elemento';
echo
echocount($componente)
count($componente)==
==11?? ok
ok\n
\n: :no
nook
ok\n;
\n;
//Sadas:
//Sadas:
////ok
ok
//ok
//ok
?>
?>

Primeiro teste de unidade

Testando o vetor parte 4:

Agora fatoramos a sada de comparao dos valores esperado e real


em uma funo que gera uma Exception onde h uma discrepncia.
Isso nos traz dois benefcios: a escrita dos testes se torna mais fcil e
s obteremos sada quando algo estiver errado.

<?php
<?php
$componente
$componente==array();
array();
assertTrue(count($componente)
assertTrue(count($componente)==
==0);
0);
$componente[]
$componente[]=='elemento';
'elemento';
assertTrue(count($componente)
assertTrue(count($componente)==
==1);
1);
function
functionassertTrue($condicao)
assertTrue($condicao)
{{
ifif(!$condicao)
(!$condicao){{
throw
thrownew
newException('Assero
Exception('Asserofalhou.');
falhou.');
}}
}}
//Sadas:
//Sadas:...
...

Primeiro teste de unidade

O teste agora est totalmente automatizado. Em vez de


apenas testar como fizemos em nossa primeira verso,
com esta verso temos um teste automatizado.
At agora s tivemos dois testes para o vetor e a funo
count() . Quando comearmos a testar as numerosas
funes array_*() que o PHP oferece, precisaremos
escrever um teste para cada uma delas.
Porm, muito melhor utilizar o que j existe. Existe um
framework com todos esses testes j prontos para
utilizao, este cara o PHPUnit.

PHPUnit

O que o PHPUnit?

um framework open source que automatiza os


testes de unidade, executando uma bateria de testes
para os desenvolvedores.

PHPUnit

Objetivos

O PHPUnit tem objetivos de fazer os testes escritos


serem:

Fcil de aprender a escrever;


Fceis de escrever;
Fceis de ler;
Fceis de executar;
Rpidos de executar;
Isolados;
Combinveis.

PHPUnit

Instalao

Em uma rede sem um maldito proxy que bloqueia


https utilizar os seguintes comandos no terminal:

pear config-set auto_discover 1


pear install pear.phpqatools.org/phpqatools

Asseres

A maioria dos casos de teste escrito para PHPUnit so


derivadas
indiretamente
da
classe
PHPUnit_Framework_Assert, que contm mtodos para
verificar automaticamente os valores e relatrios de
discrepncias.

Asseres

assertEquals

assertEquals(misto $esperado, misto $real[, string


$mensagem = ''])

Relata um erro identificado por $mensagem se as


duas variveis $esperado e $real no forem iguais.

<?php
<?php
class
classIgualaTest
IgualaTestextends
extendsPHPUnit_Framework_TestCase
PHPUnit_Framework_TestCase
{{
public
publicfunction
functiontestFalha()
testFalha()
{{
$this->assertEquals(1,
$this->assertEquals(1,0);
0);
}}
}}
?>
?>

Asseres

assertFalse

assertFalse(booleano $condicao[, string $mensagem


= ''])

Relata um erro identificado por $mensagem se


$condicao for TRUE.

<?php
<?php
class
classFalsoTest
FalsoTestextends
extendsPHPUnit_Framework_TestCase
PHPUnit_Framework_TestCase
{{
public
publicfunction
functiontestFalha()
testFalha()
{{
$this->assertFalse(TRUE);
$this->assertFalse(TRUE);
}}
}}
?>
?>

Asseres

assertInstanceOf

assertInstanceOf($esperado, $real[, $mensagem = ''])

Relata um erro identificado por $mensagem se $real


no for uma instncia de $esperado.

<?php
<?php
class
classInstanciaDeTest
InstanciaDeTestextends
extendsPHPUnit_Framework_TestCase
PHPUnit_Framework_TestCase
{{
public
publicfunction
functiontestFalha()
testFalha()
{{
$this->assertInstanceOf('RuntimeException',
$this->assertInstanceOf('RuntimeException',new
newException);
Exception);
}}
}}
?>
?>

Asseres

assertCount()

assertCount($contaEsperada,
$mensagem = ''])

$bateria[,

Relata um erro identificado por $mensagem se o


nmero de elementos em $bateria no for
$contaEsperada.

<?php
<?php
class
classContaTest
ContaTestextends
extendsPHPUnit_Framework_TestCase
PHPUnit_Framework_TestCase
{{
public
publicfunction
functiontestFalha()
testFalha()
{{
$this->assertCount(0,
$this->assertCount(0,array('foo'));
array('foo'));
}}
}}
?>
?>

string

Asseres

assertEmpty()

assertEmpty(misto $real[, string $mensagem = ''])

Relata um erro identificado por $mensagem se $real


no estiver vazio.

<?php
<?php
class
classVazioTest
VazioTestextends
extendsPHPUnit_Framework_TestCase
PHPUnit_Framework_TestCase
{{
public
publicfunction
functiontestFalha()
testFalha()
{{
$this->assertEmpty(array('foo'));
$this->assertEmpty(array('foo'));
}}
}}
?>
?>

Asseres

assertNull

assertNull(misto $variavel[, string $mensagem = ''])

Relata um erro identificado por $mensagem se


$variavel no for NULL.

<?php
<?php
class
classNuloTest
NuloTestextends
extendsPHPUnit_Framework_TestCase
PHPUnit_Framework_TestCase
{{
public
publicfunction
functiontestFalha()
testFalha()
{{
$this->assertNull('foo');
$this->assertNull('foo');
}}
}}
?>
?>

Asseres

assertTrue

assertTrue(booleano $condicao[, string $mensagem =


''])

Relata um erro identificado por $mensagem se


$condicao is FALSE.

<?php
<?php
class
classVerdadeiroTest
VerdadeiroTestextends
extendsPHPUnit_Framework_TestCase
PHPUnit_Framework_TestCase
{{
public
publicfunction
functiontestFalha()
testFalha()
{{
$this->assertTrue(FALSE);
$this->assertTrue(FALSE);
}}
}}
?>
?>

Asseres

Mais asseres:

http://phpunit.de/manual/3.7/pt_br/index.html

Exemplo prtico

O problema dos nmeros romanos

O problema dos nmeros romanos

Numerais romanos foram criados na Roma Antiga e eles


foram utilizados em todo o seu imprio. Os nmeros eram
representados por sete diferentes smbolos:

I, unus, 1, (um)

V, quinque, 5 (cinco)

X, decem, 10 (dez)

L, quinquaginta, 50 (cinquenta)

C, centum, 100 (cem)

D, quingenti, 500 (quinhentos)

M, mille, 1.000 (mil)

O problema dos nmeros romanos

Para representar outros nmeros, os romanos


combinavam estes smbolos, comeando do algarismo
de maior valor e seguindo as regras:

Algarismos de menor ou igual valor direita so


somados ao algarismo de maior valor;

Algarismos de menor valor esquerda so subtrados


do algarismo de maior valor;

Nenhum smbolo pode ser repetido lado a lado por


mais de 3 vezes.

O problema dos nmeros romanos

Utilizando conceitos de TDD, desenvolver um software


onde dado um numeral romano, o programa deve
convert-lo para o nmero inteiro correspondente.

O problema dos nmeros romanos

Primeiro teste:
<?php
<?php
require_once
require_once'/usr/share/php/PHPUnit/TextUI/TestRunner.php';
'/usr/share/php/PHPUnit/TextUI/TestRunner.php';
require_once
require_once'ConversorDeNumeroRomano.php';
'ConversorDeNumeroRomano.php';
class
classNumerosRomanosTest
NumerosRomanosTestextends
extendsPHPUnit_Framework_TestCase
PHPUnit_Framework_TestCase{{
public
publicfunction
functiontestDeveEntenderOSimboloI()
testDeveEntenderOSimboloI(){{
$romano
$romano==new
newConversorDeNumeroRomano();
ConversorDeNumeroRomano();
$numero
$numero==$romano->converte("I");
$romano->converte("I");

}}
?>
?>

}}

$this->assertEquals(1,
$this->assertEquals(1,$numero);
$numero);

O problema dos nmeros romanos


Implementar a classe ConversorDeNumeroRomano da
maneira mais simples para que o primeiro teste passe:

<?php
<?php
class
classConversorDeNumeroRomano
ConversorDeNumeroRomano{{

}}
}}
?>
?>

public
publicfunction
functionconverte($numeroEmRomano)
converte($numeroEmRomano){{
return
return1;
1;
}}

O problema dos nmeros romanos


Adicionando o segundo teste:

public
publicfunction
functiontestDeveEntenderOSimboloI()
testDeveEntenderOSimboloI(){{
$romano
$romano==new
newConversorDeNumeroRomano();
ConversorDeNumeroRomano();
$numero
$numero==$romano->converte("I");
$romano->converte("I");
}}

$this->assertEquals(1,
$this->assertEquals(1,$numero);
$numero);

public
publicfunction
functiontestDeveEntenderOSimboloV()
testDeveEntenderOSimboloV(){{
$romano
$romano==new
newConversorDeNumeroRomano();
ConversorDeNumeroRomano();
$numero
$numero==$romano->converte("V");
$romano->converte("V");
}}

$this->assertEquals(5,
$this->assertEquals(5,$numero);
$numero);

O problema dos nmeros romanos

Implementar a classe ConversorDeNumeroRomano da


maneira mais simples para que os dois primeiros testes
passem:
<?php
<?php
class
classConversorDeNumeroRomano
ConversorDeNumeroRomano{{
public
publicfunction
functionconverte($numeroEmRomano)
converte($numeroEmRomano){{
if($numeroEmRomano
if($numeroEmRomano==
=="I")
"I")
return
return1;
1;
else
elseif($numeroEmRomano
if($numeroEmRomano==
=="V")
"V")
return
return5;
5;
else
else
return
return0;
0;
}}
}}
?>
?>

}}
}}

O problema dos nmeros romanos


Para no precisarmos utilizar vrios ifs encadeados ou um switch
case vamos armazenar todos os smbolos com seus valores em um
vetor, assim contemplamos a primeira parte, que converter os
valores quando o smbolo est sozinho.

class
classConversorDeNumeroRomano
ConversorDeNumeroRomano{{
protected
protected$converteArray;
$converteArray;
public
function
public function__construct()
__construct()
{{
$this->converteArray
$this->converteArray==array('I'
array('I'=>
=>'1',
'1','V'
'V'=>
=>'5',
'5','X'
'X'=>
=>'10',
'10',
'L'
'L'=>
=>'50',
'50','C'
'C'=>
=>'100',
'100','D'
'D'=>
=>'500',
'500',
'M'
'M'=>
=>'1000');
'1000');

}}

}}
public
publicfunction
functionconverte($numeroEmRomano)
converte($numeroEmRomano){{
return
return$this->converteArray[$numeroEmRomano];
$this->converteArray[$numeroEmRomano];
}}

O problema dos nmeros romanos

Adicionando o terceiro e quarto teste :

...
...
public
publicfunction
functiontestDeveEntenderOSimboloII()
testDeveEntenderOSimboloII(){{
$romano
$romano==new
newConversorDeNumeroRomano();
ConversorDeNumeroRomano();
$numero
=
$romano->converte("II");
$numero = $romano->converte("II");
}}

$this->assertEquals(2,
$this->assertEquals(2,$numero);
$numero);

public
publicfunction
functiontestDeveEntenderOSimboloIII()
testDeveEntenderOSimboloIII(){{
$romano
$romano==new
newConversorDeNumeroRomano();
ConversorDeNumeroRomano();
$numero
$numero==$romano->converte("III");
$romano->converte("III");
}}

$this->assertEquals(3,
$this->assertEquals(3,$numero);
$numero);

O problema dos nmeros romanos

Implementando a soluo mais simples para fazer todos


os testes passarem:

public
publicfunction
functionconverte($numeroEmRomano)
converte($numeroEmRomano){{
$acumulador
$acumulador==0;
0;
for($i
=
0;
$i
<
strlen($numeroEmRomano);
for($i = 0; $i < strlen($numeroEmRomano);$i++)
$i++){{
$acumulador
$acumulador+=
+=$this->converteArray[$numeroEmRomano[$i]];
$this->converteArray[$numeroEmRomano[$i]];
}}
return
return$acumulador;
$acumulador;
}}

O problema dos nmeros romanos

Adicionando testes com nmeros de menor valor a


esquerda e a direita:

...
...
public
publicfunction
functiontestDeveEntenderOSimboloIV()
testDeveEntenderOSimboloIV(){{
$romano
$romano==new
newConversorDeNumeroRomano();
ConversorDeNumeroRomano();
$numero
$numero==$romano->converte("IV");
$romano->converte("IV");
}}

$this->assertEquals(4,
$this->assertEquals(4,$numero);
$numero);

public
publicfunction
functiontestDeveEntenderOSimboloXI()
testDeveEntenderOSimboloXI(){{
$romano
$romano==new
newConversorDeNumeroRomano();
ConversorDeNumeroRomano();
$numero
=
$romano->converte("XI");
$numero = $romano->converte("XI");
}}

$this->assertEquals(11,
$this->assertEquals(11,$numero);
$numero);

O problema dos nmeros romanos

Implementando a soluo mais simples para fazer todos


os testes passarem:
public
publicfunction
functionconverte($numeroEmRomano)
converte($numeroEmRomano){{
$acumulador
$acumulador==0;
0;
$ultimoVizinhoDaDireita
$ultimoVizinhoDaDireita==0;
0;
for($i
for($i==strlen($numeroEmRomano)
strlen($numeroEmRomano)--1;
1;$i$i>=
>=00; ;$i--){
$i--){
echo
echo$numeroEmRomano[$i];
$numeroEmRomano[$i];
$atual
$atual==$this->converteArray[$numeroEmRomano[$i]];
$this->converteArray[$numeroEmRomano[$i]];
////se
seooda
dadireita
direitafor
formenor,
menor,oomultiplicaremos
multiplicaremos
////por
por-1
-1para
paratorn-lo
torn-lonegativo
negativo
$multiplicador
$multiplicador==1;
1;
if($atual
if($atual<<$ultimoVizinhoDaDireita)
$ultimoVizinhoDaDireita)$multiplicador
$multiplicador==-1;
-1;
$acumulador
$acumulador+=
+=$atual
$atual**$multiplicador;
$multiplicador;
////atualiza
atualizaoovizinho
vizinhoda
dadireita
direita
$ultimoVizinhoDaDireita
$ultimoVizinhoDaDireita==$atual;
$atual;
}}
return
return$acumulador;
$acumulador;
}}

O problema dos nmeros romanos

Todos os testes j passam, o algoritmo criado at ento


j atende o cenrio do teste.

O problema dos nmeros romanos

Refletindo sobre o assunto

Que vantagens temos programando assim?

Foco no teste e no na implementao;


Cdigo nasce testado;
Simplicidade;
Melhor reflexo sobre o design da classe.

Anlise e cobertura de cdigo

Como voc descobre o cdigo que ainda no foi testado


ou, em outras palavras, ainda no foi coberto por um
teste?
Como voc mede o quanto os testes esto completos?

Anlise e cobertura de cdigo

O PHPUnit permite gerar relatrios que informam quais


linhas do seu cdigo foram testadas e quais no esto
sendo utilizadas gerando estatsticas de tudo isso.
Para gerar os relatrios deve ser executado os testes
atravs do comando:
phpunit --coverage-html ./report ConversorNumeroRomanosTest.php

Brainstorming

Referncias

http://phpunit.de/manual (2013);

Bergmann, PHPUnit Manual (2005);

BECK, Kent. Test Driven Development: By Example, Addison-Wesley,


2002.
BECK ,Kent. Test Driven Development: By Example. ISBN-10:
0321146530 ISBN-13: 9780321146533. Publisher: Addison-Wesley
Professional Copyright: 2003, 220 p.
Aniche,TDD - Teste e Design no Mundo Real (2012).
KOSKELA, Lasse. Test Driven: TDD and Acceptance TDD for Java
Developers. Manning Publications. 2007, 543 p.
SOMMERVILLE, Ian. Engenharia de Software. 7. ed. So Paulo:
Addison Wesley, 2004.

Você também pode gostar