test-driven development with php

113
Test Driven Development with PHP Cezar Junior de Souza E-mail: [email protected]

Upload: cezar-souza

Post on 11-Jun-2015

835 views

Category:

Technology


3 download

DESCRIPTION

Mini Curso sobre TDD e PHPUnit, básico, ministrado no PHPSC Conf 2013.

TRANSCRIPT

Page 1: Test-Driven Development with PHP

Test Driven Development with PHP

Cezar Junior de SouzaE-mail: [email protected]

Page 2: Test-Driven Development with PHP

Quem sou eu

● Desenvolvedor PHP há 4 anos;

● Bacharel em Sistemas de Informação pela Unochapecó;

● Especialista em Engenharia e Qualidade de Software;

● Programador na Unochapecó;

Page 3: Test-Driven Development with PHP

Quem sou eu

● Tem experiência com:

– Zend Framework 1;

– Zend Framework 2;

– Ruby on Rails 3.2;

– Dojo;

– ExtJs;

– Jquery;

– Doctrine 2;

– PHPUnit;

– Gerência de projetos com SCRUM...

Page 4: Test-Driven Development with PHP

O que vamos ver

● Introdução

– O que é?

– Ciclo

– Por que devemos testar?

– Por que não testamos?

– Testes automatizados

– Testes automatizados X TDD

– Conclusão ● Teste de unidade

– Primeiro teste de unidade

Page 5: Test-Driven Development with PHP

O que vamos ver

● PHPUnit

– O que é?

– Objetivos

– Instalação

– Asserções● assertEquals● assertFalse● assertInstanceOf● assertCount● assertEmpty● assertNull● assertTrue

Page 6: Test-Driven Development with PHP

O que vamos ver

● PHPUnit

– Escrevendo Testes com PHPUnit● Dependência de testes● Provedores de dados● Testando Exceções

Page 7: Test-Driven Development with PHP

O que vamos ver

● Exemplos

– Exemplo 1: Conta Bancária

– Exemplo 2: Carrinho de compras

– Exemplo 3: Nota fiscal● Mocks

● Análise e cobertura de código

● Coding Dojo

– O problema dos números romanos● Brainstorming

● Referências

Page 8: Test-Driven Development with PHP

Introdução

● O que é TDD?

– É uma das práticas 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 implementação”.

Page 9: Test-Driven Development with PHP

Introdução

● O que é TDD?

– É desenvolvido organicamente, com o feedback do código executável exibido entre as decisões.

– O desenvolvedor escreve os próprios testes porque não pode esperar 20 vezes por dia por alguém para escrevê-los.

– Utilizando a técnica as baterias de testes tendem a ser maiores, cobrindo mais casos, e garantindo uma maior qualidade externa

Page 10: Test-Driven Development with PHP

Introdução

● O que é o TDD?

– A prática nos ajuda a escrever um software melhor, com mais qualidade, e um código melhor, mais fácil de ser mantido e evoluído.

Page 11: Test-Driven Development with PHP

Introdução

“Toda prática que ajuda a aumentar a qualidade do software produzido deve ser

estudada.”(Aniche, 2012)

Page 12: Test-Driven Development with PHP

Ciclo

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

Page 13: Test-Driven Development with PHP

Por que devemos testar?

● É necessária somente uma resposta para esta pergunta, para ter a certeza que o nosso código faz o que deve fazer.

● A quantidade de software que não funciona é incrível.

Page 14: Test-Driven Development with PHP

Por que devemos testar?

● Os Estados Unidos estimam que bugs de software lhes custam aproximadamente 60 bilhões de dólares por ano...

Fonte: Computer World. Study: Buggy software costs users, vendors nearly 60b annu-ally. http://www.computerworld.com/s/article/72245/Study_Buggy_software_costs_users_vendors_nearly_60B_annually.

Page 15: Test-Driven Development with PHP

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 remédios errou.

Page 16: Test-Driven Development with PHP

Por que não testamos?

● Não há um desenvolvedor que não saiba que a solução para o problema é testar seus códigos.

● Não testamos, porque testar sai caro.

● Testar sai caro porque estamos pagando “a pessoa” errada para fazer o trabalho.

Page 17: Test-Driven Development with PHP

Por que não testamos?

É interessante a quantidade de tempo que gastamos criando soluções tecnológicas para resolver problemas “dos outros”.

Por que não escrevemos programas que resolvam também os nossos problemas?

Page 18: Test-Driven Development with PHP

Testes automatizados

● Uma maneira para conseguir testar o sistema todo de maneira constante e contínua a um preço justo é automatizando os testes.

● O teste automatizado executaria muito rápido;

● Se ele executa rápido, logo o rodaríamos constantemente;

● Se os rodarmos o tempo todo, descobriríamos os problemas mais cedo, diminuindo o custo que o bug geraria.

Page 19: Test-Driven Development with PHP

Testes automatizados

● Mas a equipe de desenvolvimento não gastará tempo escrevendo código de teste?

● Antes ela só gastava tempo com código de produção, essa equipe ficará menos produtiva?

Page 20: Test-Driven Development with PHP

Testes automatizados

● A resposta para essa pergunta é:

– Se produtividade for medida através do número de linhas de código de produção escritos por dia, talvez o desenvolvedor seja sim menos produtivo, mas, se produtividade for a quantidade de linhas de código de produção sem defeitos escritos por dia, o desenvolvedor será mais produtivo ao utilizar testes automatizados.

O que é produtividade?

Page 21: Test-Driven Development with PHP

Testes automatizados X TDD

● Teste automatizado

Problema Pensa em uma solução

Codifica a solução

Codifica ostestes

Pensa nosPossíveis casos

De erro

Problema Pensa em uma solução

Pensa nosPossíveis casos

De erro

Codifica a solução

Codifica ostestes

Testa

● TDD

Refatora

Testa Refatora

Page 22: Test-Driven Development with PHP

Conclusão

● Um médico, ao longo de uma cirurgia, nunca abre mão de qualidade. Se o paciente falar para ele: “Doutor, o senhor poderia não lavar a mão e terminar a cirurgia 30 minutos mais cedo?”, tenho certeza que o médico negaria na hora. Ele saberia que chegaria ao resultado final mais rápido, mas a chance de um problema é tão grande, que simplesmente não valeria a pena.

Page 23: Test-Driven Development with PHP

Conclusão

● Em nossa área, é justamente o contrário.

● Qual desenvolvedor nunca escreveu um código de má qualidade de maneira consciente?

● Quem nunca escreveu uma “gambiarra"?

● Quem nunca colocou software em produção sem executar o mínimo suficiente de testes para tal?

Page 24: Test-Driven Development with PHP

Conclusão

● Não há desculpas para não testar software.

● A solução para que seus testes sejam sustentáveis é automatizando;

● Testar é divertido, aumenta a qualidade do seu produto, e pode ainda ajudá-lo a identificar trechos de código que foram mal escritos ou projetados;

● Te livram várias vezes da chatice do seu inimigo natural, o “testador”;

● Enfim, é muita vantagem.

TestadorProgramadores

Page 25: Test-Driven Development with PHP

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 não 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.

Page 26: Test-Driven Development with PHP

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.

Page 27: Test-Driven Development with PHP

Teste de unidade

● Desenvolvedores gastam toda sua vida automatizando processos de outras áreas de negócio, criando sistemas para RHs, controle de caixa, entre outros, com o intuito de facilitar a vida daqueles profissionais.

● Testes automatizados são fundamentais para um desenvolvimento de qualidade, sua existência traz diversos benefícios, como aumento da qualidade e a diminuição de bugs em produção.

Por que não criar software que automatize o seu próprio ciclo de trabalho?

Page 28: Test-Driven Development with PHP

Primeiro teste de unidade

● Neste primeiro teste vamos de um simples código 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 função count();

● Para um vetor recém criado esperamos que a função count retorne 0;

● Após adicionarmos um elemento, count deverá retornar 1;

Page 29: Test-Driven Development with PHP

Primeiro teste de unidade

Page 30: Test-Driven Development with PHP

Primeiro teste de unidade

● Testando o vetor parte 1:

<?php$componente = array();// espera-se que $componente esteja vazio.

$componente[] = 'elemento';// espera-se que $componente contenha um elemento.?>

<?php$componente = array();// espera-se que $componente esteja vazio.

$componente[] = 'elemento';// espera-se que $componente contenha um elemento.?>

Page 31: Test-Driven Development with PHP

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 função count se comporta como o esperado.

<?php$componente = array();echo count($componente). “\n”;

$componente[] = 'elemento';echo count($componente). “\n”;

//Saídas:// 0 //1?>

<?php$componente = array();echo count($componente). “\n”;

$componente[] = 'elemento';echo count($componente). “\n”;

//Saídas:// 0 //1?>

Page 32: Test-Driven Development with PHP

Primeiro teste de unidade

● Testando o vetor parte 3– Vamos mudar de testes que exigem interpretação manual para testes

que podem executar automaticamente. Escrevemos a comparação do valor esperado e do real em nosso código de teste e imprimimos ok se os valores forem iguais. Se alguma vez virmos uma mensagem não ok saberemos que algo está errado.

<?php$componente = array();echo count($componente) == 0 ? “ok \n” : “não ok \n”;

$componente[] = 'elemento';echo count($componente) == 1 ? “ok \n” : “não ok \n”;

//Saídas:// ok//ok?>

<?php$componente = array();echo count($componente) == 0 ? “ok \n” : “não ok \n”;

$componente[] = 'elemento';echo count($componente) == 1 ? “ok \n” : “não ok \n”;

//Saídas:// ok//ok?>

Page 33: Test-Driven Development with PHP

Primeiro teste de unidade

● Testando o vetor parte 4:– Agora fatoramos a saída de comparação dos valores esperado e real

em uma função que gera uma Exception onde há uma discrepância. Isso nos traz dois benefícios: a escrita dos testes se torna mais fácil e só obteremos saída quando algo estiver errado.

<?php$componente = array();assertTrue(count($componente) == 0);

$componente[] = 'elemento';assertTrue(count($componente) == 1);

function assertTrue($condicao){

if (!$condicao) {throw new Exception('Asserção falhou.');

}}//Saídas: ...

<?php$componente = array();assertTrue(count($componente) == 0);

$componente[] = 'elemento';assertTrue(count($componente) == 1);

function assertTrue($condicao){

if (!$condicao) {throw new Exception('Asserção falhou.');

}}//Saídas: ...

Page 34: Test-Driven Development with PHP

Primeiro teste de unidade

● O teste agora está totalmente automatizado. Em vez de apenas testar como fizemos em nossa primeira versão, com esta versão temos um teste automatizado.

● Até agora só tivemos dois testes para o vetor e a função count() . Quando começarmos a testar as numerosas funções array_*() que o PHP oferece, precisaremos escrever um teste para cada uma delas.

● Porém, é muito melhor utilizar o que já existe. Existe um framework com todos esses testes já prontos para utilização, este cara é o PHPUnit.

Page 35: Test-Driven Development with PHP

PHPUnit

● O que é o PHPUnit?

– É um framework open source que automatiza os testes de unidade, executando uma bateria de testes para os desenvolvedores.

Page 36: Test-Driven Development with PHP

PHPUnit

● Objetivos

– O PHPUnit tem objetivos de fazer os testes escritos serem:

● Fácil de aprender a escrever;● Fáceis de escrever;● Fáceis de ler;● Fáceis de executar;● Rápidos de executar;● Isolados;● Combináveis.

Page 37: Test-Driven Development with PHP

PHPUnit

● Instalação

– Composer:● Adicionar o phpunit como uma dependência local

por projeto no arquivo composer.json:

{"require-dev": {

"phpunit/phpunit": "3.7.*"}

}

Page 38: Test-Driven Development with PHP

PHPUnit

● Instalação

– Pear● Também existe a possibilidade de instalar o

PHPUnit pelo pear, a nível de S.O

pear config-set auto_discover 1pear install pear.phpunit.de/PHPUnit

Page 39: Test-Driven Development with PHP

Asserções

● A maioria dos casos de teste escrito para PHPUnit são derivadas indiretamente da classe PHPUnit_Framework_Assert, que contém métodos para verificar automaticamente os valores e relatórios de discrepâncias.

Page 40: Test-Driven Development with PHP

Asserções

● assertEquals

– assertEquals($esperado, $real)

– Relata um erro se as variáveis $esperado e $real não forem iguais.

<?phpclass IgualaTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertEquals(1, 0);}

}?>

<?phpclass IgualaTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertEquals(1, 0);}

}?>

Page 41: Test-Driven Development with PHP

Asserções

● assertFalse

– assertFalse(booleano $condicao)

– Relata um erro se a $condicao for TRUE.

<?phpclass FalsoTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertFalse(TRUE);}

}?>

<?phpclass FalsoTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertFalse(TRUE);}

}?>

Page 42: Test-Driven Development with PHP

Asserções

● assertInstanceOf

– assertInstanceOf($esperado, $real)

– Relata um erro se $real não for uma instância de $esperado.

<?phpclass InstanciaDeTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertInstanceOf('RuntimeException', new Exception);}

}?>

<?phpclass InstanciaDeTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertInstanceOf('RuntimeException', new Exception);}

}?>

Page 43: Test-Driven Development with PHP

Asserções

● assertCount()

– assertCount($contaEsperada, $colecao)

– Relata um erro se o número de elementos em $colecao não for $contaEsperada.

<?phpclass ContaTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertCount(0, array('foo'));}

}?>

<?phpclass ContaTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertCount(0, array('foo'));}

}?>

Page 44: Test-Driven Development with PHP

Asserções

● assertEmpty()

– assertEmpty($colecao)

– Relata um erro se $colecao não estiver vazio.

<?phpclass VazioTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertEmpty(array('foo'));}

}?>

<?phpclass VazioTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertEmpty(array('foo'));}

}?>

Page 45: Test-Driven Development with PHP

Asserções

● assertNull

– assertNull($variavel)

– Relata um erro se $variavel não for NULL.

<?phpclass NuloTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertNull('foo');}

}?>

<?phpclass NuloTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertNull('foo');}

}?>

Page 46: Test-Driven Development with PHP

Asserções

● assertTrue

– assertTrue(booleano $condicao)

– Relata um erro se $condicao é FALSE.

<?phpclass VerdadeiroTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertTrue(FALSE);}

}?>

<?phpclass VerdadeiroTest extends PHPUnit_Framework_TestCase{

public function testFalha(){

$this->assertTrue(FALSE);}

}?>

Page 47: Test-Driven Development with PHP

Asserções

● Mais asserções:

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

Page 48: Test-Driven Development with PHP

Escrevendo Testes com PHPUnit

● Testando o vetor com PHPUnit

<?phpclass VetorTest extends PHPUnit_Framework_TestCase{

public function testVazio(){

$componente = array();$this->assertEquals(0, count($componente));

}

public function testPopulado(){

$componente[] = 'elemento';$this->assertInternalType('array', $componente);$this->assertEquals(1, count($componente));

}}

<?phpclass VetorTest extends PHPUnit_Framework_TestCase{

public function testVazio(){

$componente = array();$this->assertEquals(0, count($componente));

}

public function testPopulado(){

$componente[] = 'elemento';$this->assertInternalType('array', $componente);$this->assertEquals(1, count($componente));

}}

Page 49: Test-Driven Development with PHP

Escrevendo Testes com PHPUnit

● Dependência de testes– O PHPUnit suporta a declaração explícita de dependências entre

métodos de teste, ou seja, você pode informar ao interpretador que um teste depende de outro para funcionar.

Exemplo

Page 50: Test-Driven Development with PHP

● Provedores de dados

– Um método de teste pode aceitar argumentos arbitrários. Esses argumentos devem ser fornecidos por um método provedor de dados

– O método provedor de dados a ser usado é especificado usando a anotação @dataProvider.

Escrevendo Testes com PHPUnit

Exemplo

Page 51: Test-Driven Development with PHP

● Testando Exceções

– Usa-se a anotação @expectedException para testar se uma exceção foi lançada dentro do código de teste.

Escrevendo Testes com PHPUnit

Exemplo

Page 52: Test-Driven Development with PHP

Exemplo 1: Conta Bancaria

● Vamos utilizar os conceitos de TDD em uma classe que representa uma conta bancária. O contrato para a classe ContaBancaria não apenas exige métodos get e set para o saldo da conta, mas também métodos para depositar e sacar dinheiro. Também especifica as duas seguintes condições que devem ser garantidas:

– O saldo inicial da conta bancária deve ser zero.

– O saldo da conta bancária não pode se tornar negativo.

Page 53: Test-Driven Development with PHP

● Como estamos utilizando os conceitos de TDD vamos escrevemos os testes para a classe ContaBancaria antes de escrevermos o código propriamente dito da classe. Nós usamos as condições do contrato como base para os testes e nomeamos os testes conforme o mesmo.

Exemplo 1: Conta Bancaria

Teste para a classe conta bancaria

Page 54: Test-Driven Development with PHP

Exemplo 1: Conta Bancaria

● Agora vamos escrever somente o mínimo de código necessário para o primeiro teste, testSaldoInicialZero(), passar. Em nosso exemplo essa quantidade equivale a implementar o método getSaldo() da classe ContaBancaria.

Page 55: Test-Driven Development with PHP

Exemplo 1: Conta Bancaria

<?phpclass ContaBancaria{

protected $saldo = 0;public function getSaldo(){

return $this->saldo;}

}?>

<?phpclass ContaBancaria{

protected $saldo = 0;public function getSaldo(){

return $this->saldo;}

}?>

Page 56: Test-Driven Development with PHP

● O teste para a primeira condição do contrato agora passa, mas os testes para a segunda condição do contrato falham, porque ainda temos que implementar os métodos que esses testes chamam.

● Para que os testes que asseguram a segunda condição do contrato passem, agora precisamos implementar os métodos sacarDinheiro(), depositarDinheiro(), e setSaldo().

Exemplo 1: Conta Bancaria

Exemplo conta bancaria

Page 57: Test-Driven Development with PHP

Exemplo 1: Conta Bancaria

● Agora os testes que asseguram a segunda condição do contrato também passam.

Page 58: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● Neste exemplo vamos discutir como os testes podem efetivamente ajudar desenvolvedores a pensar melhor em relação as classes que estão criando.

● Vamos supor que o nosso projeto atual seja uma loja on-line. Esta loja possuí um carrinho de compras que guarda uma coleção de itens comprados.

● Um item tem as seguintes propriedades: descrição, quantidade e valor unitário. O item também deve ter um método que retorne o valor total.

Page 59: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● As classes Item e Carrinho estão desta maneira:

– Item

– CarrinhoDeCompras

Page 60: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● O cliente solicitou uma funcionalidade que devolva o valor do item de maior valor dentro desse carrinho de compras. Já pensando nos testes, temos os seguintes cenários:

– Um carrinho sem nenhum item deve retornar zero;

– Se o carrinho só tiver um item, ele mesmo será o item de maior valor;

– Se o carrinho tiver muitos itens, o item de maior valor é o que deve ser retornado.

Page 61: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● Seguindo a técnica do TDD, vamos começar pelo cenário mais simples, que nesse caso é o carrinho vazio. Vamos criar um teste para a classe MaiorPreco, responsável por essa tarefa:

Page 62: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

<?php

require getcwd().'/carrinhodecompras/src/CarrinhoDeCompras.php';require getcwd().'/carrinhodecompras/src/Item.php';require getcwd().'/carrinhodecompras/src/MaiorPreco.php';

class MaiorPrecoTest extends PHPUnit_Framework_TestCase{

public function testDeveRetornarZeroSeCarrinhoVazio(){

$CarrinhoDeCompras = new CarrinhoDeCompras();

$MaiorPreco = new MaiorPreco();$valor = $MaiorPreco->encontra($CarrinhoDeCompras);$this->assertEquals(0.0, $valor);

}}

Page 63: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● Fazer este teste passar é muito simples, basta retornar 0;

<?php class MaiorPreco{

public function encontra(CarrinhoDeCompras $carrinho){return 0;

}

}?>

Page 64: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● O teste deverá passar, agora vamos escrever o teste que é o caso do carrinho conter apenas um produto:

public function testDeveRetornarValorDoItemSeCarrinhoCom1Elemento(){$CarrinhoDeCompras = new CarrinhoDeCompras();$carrinho = $CarrinhoDeCompras->adiciona(new Item("Kindle", 1, 299.00));

$MaiorPreco = new MaiorPreco();$valor = $MaiorPreco->encontra($carrinho);$this->assertEquals(299.00, $valor);

}

Page 65: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● Para este teste passar, vamos ter que escrever um pouco mais de código, mas ele continua sendo simples:

<?php class MaiorPreco{

public function encontra(CarrinhoDeCompras $carrinho){

if(count($carrinho->getItens()) == 0)return 0;

$itens = $carrinho->getItens();return $itens[0]->getValorTotal();

}

}

Page 66: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● E finalmente, o cenário que resta, precisamos encontrar o item de maior valor caso o carrinho contenha muitos itens, vamos adicionar mais este teste para a classe:

public function testDeveRetornarMaiorValorSeCarrinhoContemMuitosElementos(){$CarrinhoDeCompras = new CarrinhoDeCompras();$carrinho = $CarrinhoDeCompras->adiciona(new Item("Kindle", 2, 299.00));$carrinho = $CarrinhoDeCompras->adiciona(new Item("Galaxy", 1, 1400.00));$carrinho = $CarrinhoDeCompras->adiciona(new Item("Google Glass", 1, 1100.00));

$MaiorPreco = new MaiorPreco3();$valor = $MaiorPreco->encontra($carrinho);

$this->assertEquals(1400.00, $valor);}

Page 67: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● Para este teste passar, na implementação vamos navegar pelos itens da coleção, procurando pelo item de maior valor total:

public function encontra(CarrinhoDeCompras $carrinho){if(count($carrinho) == 0)

return 0;$maior = 0;foreach($carrinho->getItens() as $item){

if($maior < $item->getValorTotal())$maior = $item->getValorTotal();

}return $maior;

}

Page 68: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● Com esta implementação, todos os testes devem passar.

Page 69: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● Com os testes passando, vamos avaliá-los:

– 1º: O teste instancia a classe MaiorPreco, passa para ela um objeto carrinho e verifica o retorno do método;

– 2º: Podemos ver que todo o cenário montado foi em cima da classe CarrinhoDeCompras, não fazemos nada na classe MaiorPreco;

– 3º: Isso é um mau sinal, a classe MaiorPreco parece inútil já que não houve necessidade de setar atributos ou qualquer outro dado nela;

Page 70: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● Por que não implementamos o método encontra dentro da própria classe carrinho?

● Vamos fazer essa mudança, vamos levar a lógica do método encontra dentro do CarrinhoDeCompras, o método ficará assim:

Page 71: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

public function maiorValor(){if(count($this->itens) == 0)

return 0;$maior = 0;foreach($this->itens as $item){

if($maior < $item->getValorTotal())$maior = $item->getValorTotal();

}return $maior;

}

Page 72: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● E os testes:

– CarrinhoDeComprasTest

Page 73: Test-Driven Development with PHP

Exemplo 2: Carrinho de compras

● Agora nosso teste está muito mais claro;

● Ao visualizarmos um teste com uma característica estranha, mudamos nosso design de classes;

● Muitos desenvolvedores afirmam que o TDD pode guiá-los no projeto de classes, mas isso não vai depender muito mais da experiência e conhecimento do desenvolvedor do que a utilização da técnica.

Page 74: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

● Neste exemplo, vamos, de maneira simplificada, simular a emissão de uma nota fiscal. Uma possível representação de Nota Fiscal e Pedido pode ser semelhante a estas classes:

– NotaFiscal

– Pedido

Page 75: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

● Vamos imaginar que o processo consiste em gerar a nota, persistir no banco de dados e enviá-la por e-mail;

● Para isso, seguindo os princípios de responsabilidade única, vamos precisar de três outras classes, uma responsável por enviar o e-mail, outra por persistir os dados no banco de dados e mais uma para gerar o cálculo do valor da nota fiscal;

Page 76: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

● Para melhor compreendimento do exemplo, vamos simplificar às classes de persistência e de e-mail, elas ficaram desta forma:

– Persiste.php

<?php

class Persiste{

public function persistir($nf){//persiste os dados em uma base de dados

}

}

?>

Page 77: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

– Email.php

<?php

class Email{

public function enviar($nf){//Envia nota para o cliente ou outro sistema

}

}

Page 78: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

● Agora vamos iniciar a escrita da classe GeradorDeNotaFiscal;

● Vamos imaginar que a regra de cálculo da nota fiscal seja simples, o valor da nota deve ser 18% do valor do pedido;

● Vamos começar pelo teste

Page 79: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

<?php

require getcwd().'/notafiscal/src/GeradorDeNotaFiscal.php';require getcwd().'/notafiscal/src/Pedido.php';

class GeradorDeNotaFiscalTest extends PHPUnit_Framework_TestCase{

public function testDeveGerarNFComValorDeImpostoDescontado(){$GeradorDeNotaFiscal = new GeradorDeNotaFiscal();$Pedido = new Pedido();$Pedido->setPedido("Bob", 4000, 1);

$NotaFiscal = $GeradorDeNotaFiscal->gerar($Pedido);

$this->assertEquals(4000 * 0.82, $NotaFiscal->getValor());

}

Page 80: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

● Fazer este teste passar é fácil, basta instanciar uma nota fiscal com 18% a menos do valor do pedido:

<?php

require getcwd().'/notafiscal/src/NotaFiscal.php';require getcwd().'/notafiscal/src/Persiste.php';

class GeradorDeNotaFiscal{

public function gerar($pedido){$NotaFiscal = new NotaFiscal();$NotaFiscal->setNotaFiscal($pedido->getCliente(),

$pedido->getValorTotal() * 0.82, date('Y-m-d'));

return $NotaFiscal;}

}

Page 81: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

● O teste passa. O próximo passo é persistir os dados dessa nota fiscal. A classe persiste já existe, com seu método persistir(), só precisamos usá-lo. Dessa vez vamos fazer diferente, sem escrever o teste antes, vamos ver como a implementação ficaria

Page 82: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

Linha 1 Linha 2 Linha 3 Linha 40

2

4

6

8

10

12

Coluna 1

Coluna 2

Coluna 3

<?php

require getcwd().'/notafiscal/src/NotaFiscal.php';require getcwd().'/notafiscal/src/Persiste.php';

class GeradorDeNotaFiscal{

public function gerar($pedido){$NotaFiscal = new NotaFiscal();$NotaFiscal->setNotaFiscal($pedido->

getCliente(), $pedido->getValorTotal() * 0.94, date('Y-m-d'));$Persiste = new Persiste();$Persiste->persistir($NotaFiscal);

return $NotaFiscal;}

}

Page 83: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

● Ok, mas pera... a gente não implementou a classe Persiste, sua funcionalidade não é responsabilidade da classe que estamos escrevendo, então como podemos testar o seu comportamento?

Page 84: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

● Mocks

– A ideia dos testes de unidade é testar a classe de maneira isolada, sem qualquer interferência das classes que a rodeiam;

– Em um teste onde as classes estão integradas, qual classe que gerou o problema?

– Não há o porquê de os testes do GeradorDeNotaFiscalTest garantir que o dado foi persistido com sucesso, isso é tarefa da classe Persiste, a tarefa do GeradorDeNotaFiscal é somente invocar o método persistir();

Page 85: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

● Moks

– Para conseguir fazer o teste passar, vamos criar um “duble” para a classe Persiste, um clone dela, onde vamos simular o que o método persistir deve retornar;

Page 86: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

● O nosso teste de persistência da classe geradora da nota ficará desta forma:

Page 87: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

public function testDevePersistirNFGerada(){$mock = $this->mockPeriste();

$GeradorDeNotaFiscal = new GeradorDeNotaFiscal($mock);$Pedido = new Pedido();$Pedido->setPedido("Bob", 4000, 1);$NotaFiscal = $GeradorDeNotaFiscal->gerar($Pedido);

}

private function mockPeriste(){$mock = $this->getMock('Persiste', array('persistir'));$mock->expects($this->any())->method('persistir')->will($this->returnValue(true));return $mock;

}

Page 88: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

● Para conseguirmos testar utilizando o mock, a classe que simulamos deverá ser uma dependência na classe que testamos, portanto, devemos passar a classe Persiste no construtor da classe GeradorDeNotaFiscal, ela ficará assim:

Page 89: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

class GeradorDeNotaFiscal{

protected $persiste;

public function __construct($persiste){$this->persiste = $persiste;

}

public function gerar($pedido){$NotaFiscal = new NotaFiscal();$NotaFiscal->setNotaFiscal($pedido->getCliente(),

$pedido->getValorTotal() * 0.82, date('Y-m-d'));$this->persiste->persistir($NotaFiscal);

return $NotaFiscal;}

Page 90: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

● Mocks

– A arte de mockar as dependências é conhecida como “TDD ao estilo londrino”, isso porquê muitas das discussões importantes na área de mock objects surgiram por lá. Autores famosos como Steve Freeman e Nat Pryce (ambos britânicos) são fãs dessa abordagem.

Page 91: Test-Driven Development with PHP

Exemplo 3: Nota Fiscal

● Mocks são extremamente úteis, com ele não precisamos nos preocupar com o funcionamento de classes que ainda não estão prontas, precisamos nos preocupar somente com a unidade que estamos implementando;

● Podemos criar um mock para a classe e-mail também, mas isso deixo por conta de vocês.

Page 92: Test-Driven Development with PHP

Análise e cobertura de código

● Como você descobre o código que ainda não foi testado ou, em outras palavras, ainda não foi coberto por um teste?

● Como você mede o quanto os testes estão completos?

Page 93: Test-Driven Development with PHP

Análise e cobertura de código

● O PHPUnit permite gerar relatórios que informam quais linhas do seu código foram testadas e quais não estão sendo utilizadas gerando estatísticas de tudo isso.

● Para gerar os relatórios deve ser executado os testes através do comando:– phpvendor/phpunit/phpunit/phpunit.php –coverage-html ./report

contabancaria/test/ContaBancariaTest.php

Page 94: Test-Driven Development with PHP

Coding Dojo

● Um coding dojo é um encontro de desenvolvedores que estão interessados em aprender alguma coisa nova, como uma linguagem de programação diferente, ou técnicas de desenvolvimento diferentes. O objetivo é criar um ambiente tranquilo e favorável a novos aprendizados.

Page 95: Test-Driven Development with PHP

Coding Dojo

● A prática mais comum é juntar alguns desenvolvedores e projetar uma máquina na parede. Dois desenvolvedores sentam em frente ao computador e começam a resolver algum problema pré-determinado, usando a linguagem e/ou a prática que desejam aprender melhor.

● Geralmente esses dois desenvolvedores tem apenas alguns minutos para trabalhar. Enquanto eles trabalham a plateia assiste e eventualmente sugere alterações para o “piloto e co-piloto”. Ao final do tempo , o co-piloto vira piloto, o piloto volta para a plateia, e alguém da plateia se torna “co-piloto”.

Page 96: Test-Driven Development with PHP

Coding Dojo

● No nosso Coding Dojo, vamos resolver o problema dos números romanos utilizando os conceitos vistos até aqui.

Page 97: Test-Driven Development with PHP

O problema dos números romanos

● Numerais romanos foram criados na Roma Antiga e eles foram utilizados em todo o seu império. Os números eram representados por sete diferentes símbolos:

– 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)

Page 98: Test-Driven Development with PHP

O problema dos números romanos

● Para representar outros números, os romanos combinavam estes símbolos, começando do algarismo de maior valor e seguindo as regras:

– Algarismos de menor ou igual valor à direita são somados ao algarismo de maior valor;

– Algarismos de menor valor à esquerda são subtraídos do algarismo de maior valor;

– Nenhum símbolo pode ser repetido lado a lado por mais de 3 vezes.

Page 99: Test-Driven Development with PHP

O problema dos números romanos

● Utilizando conceitos de TDD, desenvolver um software onde dado um numeral romano, o programa deve convertê-lo para o número inteiro correspondente.

Page 100: Test-Driven Development with PHP

O problema dos números romanos

● Primeiro teste:

<?php

require getcwd().'/n_romanos/src/ConversorDeNumeroRomano.php';

class ConversorDeNumeroRomanoTest extends PHPUnit_Framework_TestCase {

public function testDeveEntenderOSimboloI() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("I");

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

}?>

<?php

require getcwd().'/n_romanos/src/ConversorDeNumeroRomano.php';

class ConversorDeNumeroRomanoTest extends PHPUnit_Framework_TestCase {

public function testDeveEntenderOSimboloI() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("I");

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

}?>

Page 101: Test-Driven Development with PHP

O problema dos números romanos

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

<?php

class ConversorDeNumeroRomano {

public function converte($numeroEmRomano) {return 1;

}}

}?>

<?php

class ConversorDeNumeroRomano {

public function converte($numeroEmRomano) {return 1;

}}

}?>

Page 102: Test-Driven Development with PHP

O problema dos números romanos

● Adicionando o segundo teste:

public function testDeveEntenderOSimboloI() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("I");

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

public function testDeveEntenderOSimboloV() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("V");

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

public function testDeveEntenderOSimboloI() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("I");

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

public function testDeveEntenderOSimboloV() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("V");

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

Page 103: Test-Driven Development with PHP

● Implementar a classe ConversorDeNumeroRomano da maneira mais simples para que os dois primeiros testes passem:

O problema dos números romanos

<?phpclass ConversorDeNumeroRomano {

public function converte($numeroEmRomano) {if($numeroEmRomano == "I")

return 1;else if($numeroEmRomano == "V")

return 5;else

return 0;

}}}?>

<?phpclass ConversorDeNumeroRomano {

public function converte($numeroEmRomano) {if($numeroEmRomano == "I")

return 1;else if($numeroEmRomano == "V")

return 5;else

return 0;

}}}?>

<?phpclass ConversorDeNumeroRomano {

public function converte($numeroEmRomano) {if($numeroEmRomano == "I")

return 1;else if($numeroEmRomano == "V")

return 5;else

return 0;

}}?>

<?phpclass ConversorDeNumeroRomano {

public function converte($numeroEmRomano) {if($numeroEmRomano == "I")

return 1;else if($numeroEmRomano == "V")

return 5;else

return 0;

}}?>

Page 104: Test-Driven Development with PHP

O problema dos números romanos

● Para não precisarmos utilizar vários ifs encadeados ou um switch case vamos armazenar todos os símbolos com seus valores em um vetor, assim contemplamos a primeira parte, que é converter os valores quando o símbolo está sozinho.

class ConversorDeNumeroRomano {protected $converteArray; public function __construct(){

$this->converteArray = array('I' => '1', 'V' => '5', 'X' => '10','L' => '50', 'C' => '100', 'D' => '500','M' => '1000');

}public function converte($numeroEmRomano) {

return $this->converteArray[$numeroEmRomano];}

}

class ConversorDeNumeroRomano {protected $converteArray; public function __construct(){

$this->converteArray = array('I' => '1', 'V' => '5', 'X' => '10','L' => '50', 'C' => '100', 'D' => '500','M' => '1000');

}public function converte($numeroEmRomano) {

return $this->converteArray[$numeroEmRomano];}

}

Page 105: Test-Driven Development with PHP

O problema dos números romanos

● Adicionando o terceiro e quarto teste :

...

public function testDeveEntenderOSimboloII() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("II");

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

public function testDeveEntenderOSimboloIII() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("III");

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

...

public function testDeveEntenderOSimboloII() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("II");

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

public function testDeveEntenderOSimboloIII() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("III");

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

Page 106: Test-Driven Development with PHP

O problema dos números romanos

● Implementando a solução mais simples para fazer todos os testes passarem:

public function converte($numeroEmRomano) {$acumulador = 0;for($i = 0; $i < strlen($numeroEmRomano); $i++) {

$acumulador += $this->converteArray[$numeroEmRomano[$i]];}return $acumulador;

}

public function converte($numeroEmRomano) {$acumulador = 0;for($i = 0; $i < strlen($numeroEmRomano); $i++) {

$acumulador += $this->converteArray[$numeroEmRomano[$i]];}return $acumulador;

}

Page 107: Test-Driven Development with PHP

O problema dos números romanos

● Adicionando testes com números de menor valor a esquerda e a direita:

...

public function testDeveEntenderOSimboloIV() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("IV");

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

public function testDeveEntenderOSimboloXI() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("XI");

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

...

public function testDeveEntenderOSimboloIV() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("IV");

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

public function testDeveEntenderOSimboloXI() {$romano = new ConversorDeNumeroRomano();$numero = $romano->converte("XI");

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

Page 108: Test-Driven Development with PHP

O problema dos números romanos

● Implementando a solução mais simples para fazer todos os testes passarem:

public function converte($numeroEmRomano) {$acumulador = 0;$ultimoVizinhoDaDireita = 0;for($i = strlen($numeroEmRomano) - 1; $i >= 0 ; $i--){

echo $numeroEmRomano[$i]; $atual = $this->converteArray[$numeroEmRomano[$i]];// se o da direita for menor, o multiplicaremos// por -1 para torná-lo negativo$multiplicador = 1;if($atual < $ultimoVizinhoDaDireita) $multiplicador = -1;$acumulador += $atual * $multiplicador;// atualiza o vizinho da direita$ultimoVizinhoDaDireita = $atual;

}return $acumulador;

}

public function converte($numeroEmRomano) {$acumulador = 0;$ultimoVizinhoDaDireita = 0;for($i = strlen($numeroEmRomano) - 1; $i >= 0 ; $i--){

echo $numeroEmRomano[$i]; $atual = $this->converteArray[$numeroEmRomano[$i]];// se o da direita for menor, o multiplicaremos// por -1 para torná-lo negativo$multiplicador = 1;if($atual < $ultimoVizinhoDaDireita) $multiplicador = -1;$acumulador += $atual * $multiplicador;// atualiza o vizinho da direita$ultimoVizinhoDaDireita = $atual;

}return $acumulador;

}

Page 109: Test-Driven Development with PHP

O problema dos números romanos

● Todos os testes já passam, o algoritmo criado até então já atende o cenário do teste.

Page 110: Test-Driven Development with PHP

● Refletindo sobre o assunto

– Que vantagens temos programando assim?● Foco no teste e não na implementação;● Código nasce testado;● Simplicidade;● Melhor reflexão sobre o design da classe.

O problema dos números romanos

Page 111: Test-Driven Development with PHP

Brainstorming

Page 112: Test-Driven Development with PHP

Dúvidas?

[email protected]

● http://www.slideshare.net/cezar08

Page 113: Test-Driven Development with PHP

Referências

● 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. São Paulo: Addison Wesley, 2004.