55
Testes de unidade avançados e TDD Testes de unidade avançados e TDD Luís Otávio Cobucci Oblonczyk 21 de Outubro de 2011 6° SoLiSC

Testes de unidade e TDD SoLiSC 2011

Embed Size (px)

DESCRIPTION

Aqui buscamos ir um pouco além do uso básico do PHPUnit para que com o uso do TDD garantirmos o funcionamento correto de suas regras de negócio.

Citation preview

Testes de unidade avançados e TDD

Testes de unidade avançados e TDD

Luís Otávio Cobucci Oblonczyk

21 de Outubro de 20116° SoLiSC

Luís Otávio Cobucci OblonczykLuís Otávio Cobucci Oblonczyk

● Desenvolvedor PHP na Softnex Tecnologia● Orientador no Senac TI● Doido por PHP desde 2003● Perfeccionista ao extremo =P

@lcobucci

http://about.me/lcobucci

Normalmente quando desenvolvemos fazemos o teste F5

Normalmente quando desenvolvemos fazemos o teste F5

1) Abre sistema no browser

Normalmente quando desenvolvemos fazemos o teste F5

1) Abre sistema no browser2) Procura o erro

Normalmente quando desenvolvemos fazemos o teste F5

1) Abre sistema no browser2) Procura o erro3) Muda o código

Normalmente quando desenvolvemos fazemos o teste F5

1) Abre sistema no browser2) Procura o erro3) Muda o código4) F5

Normalmente quando desenvolvemos fazemos o teste F5

1) Abre sistema no browser2) Procura o erro3) Muda o código4) F55) Muda o código

Normalmente quando desenvolvemos fazemos o teste F5

1) Abre sistema no browser2) Procura o erro3) Muda o código4) F55) Muda o código6) F5

Normalmente quando desenvolvemos fazemos o teste F5

1) Abre sistema no browser2) Procura o erro3) Muda o código4) F55) Muda o código6) F5…

Normalmente quando desenvolvemos fazemos o teste F5

1) Abre sistema no browser2) Procura o erro3) Muda o código4) F55) Muda o código6) F5…999) Resolve o

problema

Quanto tempo PERDEMOS nesta brincadeira?

Quanto dinheiro PERDEMOS nesta brincadeira?

E a pergunta que não quer calar...

Quando isso acontece num ambiente “separado”não tem GRANDES problemas

E quando acontece na “vida real”?

Tá, e o que eu posso fazer?

Tá, e o que eu posso fazer?

Automatizar alguns testes!(pra começar)

Testes de UnidadeTestes de Unidade

“Teste de unidade é toda a aplicação de teste nas assinaturas de entradas e saídas de um sistema, consiste em validar dados válidos e inválidos via I/O (entrada/saída) sendo aplicado por desenvolvedores ou analistas de teste.

Uma unidade é a menor parte testável de um programa de computador.”

http://pt.wikipedia.org/wiki/Teste_de_unidade

Testes de UnidadeTestes de Unidade

● Te dá uma visão rápida do teu código: ou funciona ou não funciona

● Facilita o desenvolvimento, pois caso alguma alteração estrague uma funcionalidade já existente, você fica sabendo antes mesmo de ser enviado para os sistemas de controle de versão

● Servem também como parte da documentação● Ajuda a manter a sanidade mental da equipe

Utilizarei aqui funcionalidadespresentes a partir do PHP 5.3.2!

Utilizarei aqui funcionalidadespresentes a partir do PHP 5.3.2!

Se você ainda não usao PHP 5.3 é uma boaoportunidade decomeçar ; )

PHPUnitPHPUnit

O que é: Framework p/ criação e execução de testes de unidade. Faz parte da família xUnit.

Criador: Sebastian Bergmann

Instalação:pear config-set auto_discover 1

pear install pear.phpunit.de/PHPUnit

http://www.phpunit.de

Bora pro código!

src/Lcobucci/Examples/Tests/Calculator.php

<?phpnamespace Lcobucci\Examples\Tests;

class Calculator{ public function sum($number1, $number2) { return $number1 + $number2; }}

test/Lcobucci/Examples/Tests/CalculatorTest.php<?phprequire_once 'src/Lcobucci/Examples/Tests/Calculator.php';require_once 'PHPUnit/Framework/TestCase.php';

use \Lcobucci\Examples\Tests\Calculator;

class CalculatorTest extends PHPUnit_Framework_TestCase{ public function testSum() { $calculator = new Calculator(); $this->assertEquals(4, $calculator->sum(2, 2)); }}

TestCase e TestSuiteTestCase e TestSuite

● O TestCase possui métodos de testes para um recurso (normalmente uma classe)

● TestSuites formam grupos de testes, ou seja dentro de um TestSuite podem ter vários TestCases ou até vários TestSuites

tearDown() e setUp()tearDown() e setUp()

● São métodos protegidos (com visibilidade protected) disponíveis tanto no TestCase quanto no TestSuite

● Podem ser sobrecarregados caso NECESSÁRIO, e são utilizados para preparar e limpar o ambiente de teste

● No TestCase o setUp() é executado ANTES de cada método de teste e o tearDown ao FINAL de cada método

tearDown() e setUp()tearDown() e setUp()

● No TestSuite o setUp() é executado ANTES de cada item da suite de testes e o tearDown ao FINAL de cada item

Boas práticasBoas práticas

● Não crie métodos de teste que não teste absolutamente nada

● Não queira testar várias situações em um método só

● Use os métodos corretos para comparações● $this->assertEmpty($var); vs

$this->assertTrue(empty($var));● $this->assertInstanceOf('\Exception', $e); vs

$this->assertTrue($e instanceof \Exception);

Boas práticasBoas práticas

● Crie um método de teste para cada condição de teste de um método usando nomes significativos

● Mantenha os diretórios de teste similares aos diretórios das classes

● Use arquivo de configuração com um arquivo de bootstrap

src/Lcobucci/Examples/Tests/Calculator.php

<?phpnamespace Lcobucci\Examples\Tests;

use \InvalidArgumentException;

class Calculator{ public function div($dividend, $divisor) { if ($divisor == 0) { throw new InvalidArgumentException( 'The divisor cannot be ZERO' ); }

return $dividend / $divisor; }}

test/Lcobucci/Examples/Tests/CalculatorTest.php<?phprequire_once 'src/Lcobucci/Examples/Tests/Calculator.php';require_once 'PHPUnit/Framework/TestCase.php';

use \Lcobucci\Examples\Tests\Calculator;

class CalculatorTest extends PHPUnit_Framework_TestCase{ /** * @expectedException \InvalidArgumentException */ public function testDivisorCannotBeZero() { $calculator = new Calculator(); $calculator->div(2, 0); }

public function testDivMustReturnTheRightDivision() { $calculator = new Calculator(); $this->assertEquals(2, $calculator->div(4, 2)); }}

Test DoublesTest Doubles

● São objetos “falsos” que utilizamos para isolar elementos que podem impedir a execução dos testes de unidade a qualquer momento e em qualquer ambiente, como:● File system● Database connections● Socket connections● HTTP requests

Test DoublesTest Doubles

● Dummy: são objetos que nunca são utilizados, são criados apenas para completar a lista de parâmetros exigidos

● Fake: objetos que implementados APENAS para testes, e que NÃO DEVEM ser utilizados em produção (ex: conexão com DB em memória)

● Stubs: objetos que retornam dados falsos● Mocks: parecido com stubs, porém além de

retornar verifica número de chamadas e parâmetros utilizados

test/Lcobucci/Examples/Tests/CalculatorTest.php (EXEMPLO stub)<?phprequire_once 'src/Lcobucci/Examples/Tests/Calculator.php';require_once 'src/Lcobucci/Utils/FileLog.php';require_once 'PHPUnit/Framework/TestCase.php';

use \Lcobucci\Examples\Tests\Calculator;

class CalculatorTest extends PHPUnit_Framework_TestCase{ public function testLoggerCanBeAttached() { $logger = $this->getMock( '\Lcobucci\Utils\FileLog', array('log') );

$logger->expects($this->any()) ->method('log') ->will($this->returnValue(true));

$calculator = new Calculator($logger); $this->assertEquals(2, $calculator->div(4, 2)); }}

test/Lcobucci/Examples/Tests/CalculatorTest.php (EXEMPLO mock)<?phprequire_once 'src/Lcobucci/Examples/Tests/Calculator.php';require_once 'src/Lcobucci/Utils/FileLog.php';require_once 'PHPUnit/Framework/TestCase.php';

use \Lcobucci\Examples\Tests\Calculator;

class CalculatorTest extends PHPUnit_Framework_TestCase{ public function testMathOperationsMustBeLogged() { $logger = $this->getMock( '\Lcobucci\Utils\FileLog', array('log') );

$logger->expects($this->one()) ->method('log') ->with('div', array(4, 2), 2) ->will($this->returnValue(true));

$calculator = new Calculator($logger); $this->assertEquals(2, $calculator->div(4, 2)); }}

Outras funcionalidadesOutras funcionalidades

● Dependência entre métodos de teste● Acessar atributos privados● Acessar métodos privados

test/Lcobucci/Examples/Tests/StackTest.php<?phprequire_once 'PHPUnit/Framework/TestCase.php';

class StackTest extends PHPUnit_Framework_TestCase{ public function testStackIsInitiallyEmpty() { $stack = array(); $this->assertEmpty($stack);

return $stack; }

/** * @depends testStackIsInitiallyEmpty */ public function testAddingAnElementWorks(array $stack) { array_push($stack, 'test'); $this->assertEquals('test', $stack[count($stack) - 1]); }}

test/Lcobucci/Examples/Tests/PrivateAttributeTest.phpNão é uma boa prática, mas de vez em quando é necessário...<?phprequire_once 'PHPUnit/Framework/TestCase.php';

class PrivateAttributeTest extends PHPUnit_Framework_TestCase{ public function testPrivateAttribute() { $this->assertEquals( 'teste', $this->readAttribute( new MyClass(), 'myPrivateAttribute' ) ); }}

test/Lcobucci/Examples/Tests/PrivateAttributeTest.phpNão é uma boa prática, mas de vez em quando é necessário...<?phprequire_once 'PHPUnit/Framework/TestCase.php';

class PrivateAttributeTest extends PHPUnit_Framework_TestCase{ public function testPrivateAttribute() { $this->assertAttributeEquals( 'teste', 'myPrivateAttribute', new MyClass() ); }}

test/Lcobucci/Examples/Tests/PrivateMethodTest.phpNão é uma boa prática, mas de vez em quando é necessário...<?phprequire_once 'PHPUnit/Framework/TestCase.php';

class PrivateMethodTest extends PHPUnit_Framework_TestCase{ public function testPrivateMethod() { $obj = new MyClass(); $method = new ReflectionMethod( $obj, 'myPrivateMethod' );

$method->setAccessible(true);

$this->assertEquals( 2, $method->invoke($obj, 4, 2) ); }}

Facilitadores dos testes de unidadeFacilitadores dos testes de unidade● Baixo acoplamento● Alta coesão● Injeção de dependências● Métodos pequenos

Complicadores dos testes de unidadeComplicadores dos testes de unidade● Escopo global● Atributos e métodos estáticos● Singleton● Tarefas no construtor da classe

Test Driven DevelopmentTest Driven Development

“Test Driven Development (TDD) ou em português Desenvolvimento dirigido por testes é uma técnica de desenvolvimento de software que baseia em um ciclo curto de repetições: Primeiramente o desenvolvedor escreve um caso de teste automatizado que define uma melhoria desejada ou uma nova funcionalidade. Então, é produzido código que possa ser validado pelo teste para posteriormente o código ser refatorado para um código sob padrões aceitáveis.”

http://pt.wikipedia.org/wiki/Test_Driven_Development

Só isso??

Benefícios do TDDBenefícios do TDD

● Diminui ou até elimina a otimização prematura● Código simples, eficaz e eficiente● Cenários de testes bem definidos

Hora do exemplo!

E pra enfrentar bugs, como fica?

TDD na correção de bugsTDD na correção de bugs

1. Escreva um cenário de teste que reproduza o erro

2. Resolva o problema no código fonte (não pensa em deixar bonito, resolva o problema)

3. Execute os testes para garantir que nada foi comprometido

4. Refatore o código corrigido (agora sim pra deixar bonito)

5. Execute os testes novamente e seja feliz =D

Dúvidas???Dúvidas???

Obrigado!Obrigado!

Eu por aí: http://about.me/lcobucci

Slides: http://slideshare.net/lcobucci

Avalie essa palestra: http://joind.in/3938