O documento discute testes de unidade avançados e desenvolvimento guiado por testes (TDD). O palestrante apresenta os benefícios de automatizar testes de unidade, como reduzir erros e permitir refatorações seguras. Ele também explica conceitos como stubs, mocks e test doubles, e demonstra exemplos práticos de testes de unidade com PHPUnit.
Testes automatizados end-to-end com WordPress por Fabio Nas
Testes de unidade e TDD SoLiSC 2011
1. Testes de unidade
avançados e TDD
Luís Otávio Cobucci Oblonczyk
21 de Outubro de 2011
6° SoLiSC
2. Luí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
8. Normalmente quando desenvolvemos fazemos o teste F5
1) Abre sistema no
browser
2) Procura o erro
3) Muda o código
4) F5
5) Muda o código
9. Normalmente quando desenvolvemos fazemos o teste F5
1) Abre sistema no
browser
2) Procura o erro
3) Muda o código
4) F5
5) Muda o código
6) F5
10. Normalmente quando desenvolvemos fazemos o teste F5
1) Abre sistema no
browser
2) Procura o erro
3) Muda o código
4) F5
5) Muda o código
6) F5
…
11. Normalmente quando desenvolvemos fazemos o teste F5
1) Abre sistema no
browser
2) Procura o erro
3) Muda o código
4) F5
5) Muda o código
6) F5
…
999) Resolve o
problema
20. Tá, e o que eu posso fazer?
Automatizar alguns testes!
(pra começar)
21. Testes 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
22. Testes 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
25. PHPUnit
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
29. TestCase 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
30. 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
31. tearDown() e setUp()
●
No TestSuite o setUp() é executado ANTES de
cada item da suite de testes e o tearDown ao
FINAL de cada item
32. Boas 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);
33. Boas 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
36. Test 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
37. Test 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
38. test/Lcobucci/Examples/Tests/CalculatorTest.php (EXEMPLO stub)
<?php
require_once 'src/Lcobucci/Examples/Tests/Calculator.php';
require_once 'src/Lcobucci/Utils/FileLog.php';
require_once 'PHPUnit/Framework/TestCase.php';
use LcobucciExamplesTestsCalculator;
class CalculatorTest extends PHPUnit_Framework_TestCase
{
public function testLoggerCanBeAttached()
{
$logger = $this->getMock(
'LcobucciUtilsFileLog',
array('log')
);
$logger->expects($this->any())
->method('log')
->will($this->returnValue(true));
$calculator = new Calculator($logger);
$this->assertEquals(2, $calculator->div(4, 2));
}
}
39. test/Lcobucci/Examples/Tests/CalculatorTest.php (EXEMPLO mock)
<?php
require_once 'src/Lcobucci/Examples/Tests/Calculator.php';
require_once 'src/Lcobucci/Utils/FileLog.php';
require_once 'PHPUnit/Framework/TestCase.php';
use LcobucciExamplesTestsCalculator;
class CalculatorTest extends PHPUnit_Framework_TestCase
{
public function testMathOperationsMustBeLogged()
{
$logger = $this->getMock(
'LcobucciUtilsFileLog',
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));
}
}
40. Outras funcionalidades
●
Dependência entre métodos de teste
●
Acessar atributos privados
●
Acessar métodos privados
41. test/Lcobucci/Examples/Tests/StackTest.php
<?php
require_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]);
}
}
42. test/Lcobucci/Examples/Tests/PrivateAttributeTest.php
Não é uma boa prática, mas de vez em quando é necessário...
<?php
require_once 'PHPUnit/Framework/TestCase.php';
class PrivateAttributeTest extends PHPUnit_Framework_TestCase
{
public function testPrivateAttribute()
{
$this->assertEquals(
'teste',
$this->readAttribute(
new MyClass(),
'myPrivateAttribute'
)
);
}
}
43. test/Lcobucci/Examples/Tests/PrivateAttributeTest.php
Não é uma boa prática, mas de vez em quando é necessário...
<?php
require_once 'PHPUnit/Framework/TestCase.php';
class PrivateAttributeTest extends PHPUnit_Framework_TestCase
{
public function testPrivateAttribute()
{
$this->assertAttributeEquals(
'teste',
'myPrivateAttribute',
new MyClass()
);
}
}
44. test/Lcobucci/Examples/Tests/PrivateMethodTest.php
Não é uma boa prática, mas de vez em quando é necessário...
<?php
require_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)
);
}
}
45. Facilitadores dos testes de
unidade
●
Baixo acoplamento
●
Alta coesão
●
Injeção de dependências
●
Métodos pequenos
46. Complicadores dos testes de
unidade
●
Escopo global
●
Atributos e métodos estáticos
●
Singleton
●
Tarefas no construtor da classe
47.
48. Test 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
53. TDD 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