Prev Next

Capítulo 19. Estendendo o PHPUnit

O PHPUnit pode ser estendido de várias formas para facilitar a escrita de testes e personalizar as respostas que você recebe ao executar os testes. Aqui estão pontos de partida comuns para estender o PHPUnit.

Subclasse PHPUnit_Framework_TestCase

Escreva asserções personalizadas e métodos utilitários em uma subclasse abstrata do PHPUnit_Framework_TestCase e derive suas classes de caso de teste dessa classe. Essa é uma das formas mais fáceis de estender o PHPUnit.

Escreva asserções personalizadas

Ao escrever asserções personalizadas a melhor prática é seguir a mesma forma que as asserções do próprio PHPUnit são implementadas. Como você pode ver em Exemplo 19.1, o método assertTrue() é apenas um empacotador em torno dos métodos isTrue() e assertThat(): isTrue() cria um objeto comparador que é passado para assertThat() para avaliação.

Exemplo 19.1: Os métodos assertTrue() e isTrue() da classe PHPUnitFramework_Assert

<?php
abstract class PHPUnit_Framework_Assert
{
// ...

/**
* Asserta que uma condição é verdade.
*
* @param boolean $condicao
* @param string $menssagem
* @throws PHPUnit_Framework_AssertionFailedError
*/
public static function assertTrue($condicao, $mensagem = '')
{
self::assertThat($condicao, self::isTrue(), $mensagem);
}

// ...

/**
* Retorna um objeto equiparador PHPUnit_Framework_Constraint_IsTrue.
*
* @return PHPUnit_Framework_Constraint_IsTrue
* @since Método disponível desde a versão 3.3.0
*/
public static function isTrue()
{
return new PHPUnit_Framework_Constraint_IsTrue;
}

// ...
}?>

Exemplo 19.2 mostra como PHPUnit_Framework_Constraint_IsTrue estende a classe base abstrata para objetos comparadores (ou restritores), PHPUnit_Framework_Constraint.

Exemplo 19.2: A classe PHPUnit_Framework_Constraint_IsTrue

<?php
class PHPUnit_Framework_Constraint_IsTrue extends PHPUnit_Framework_Constraint
{
/**
* Avalia a restrição para o parâmetro $outro. Retorna TRUE se a
* restrição é confirmada, FALSE caso contrário.
*
* @param misto $outro Valor ou objeto a avaliar.
* @return bool
*/
public function matches($outro)
{
return $outro === TRUE;
}

/**
* Retorna uma representação string da restrição.
*
* @return string
*/
public function toString()
{
return 'é verdade';
}
}?>

O esforço de implementar os métodos assertTrue() e isTrue() assim como a classe PHPUnit_Framework_Constraint_IsTrue rende o benefício de que assertThat() automaticamente cuida de avaliar a asserção e escriturar tarefas como contá-las para estatísticas. Além disso, o método isTrue() pode ser usado como um comparador ao configurar objetos falsos.

Implementando PHPUnit_Framework_TestListener

Exemplo 19.3 mostra uma implementação simples da interface PHPUnit_Framework_TestListener.

Exemplo 19.3: Um simples ouvinte de teste

<?php
class SimplesOuvinteDeTest implements PHPUnit_Framework_TestListener
{
public function addError(PHPUnit_Framework_Test $teste, Exception $e, $tempo)
{
printf("Erro ao executar o teste '%s'.\n", $teste->getName());
}

public function addFailure(PHPUnit_Framework_Test $teste, PHPUnit_Framework_AssertionFailedError $e, $tempo)
{
printf("O Teste '%s' falhou.\n", $teste->getName());
}

public function addIncompleteTest(PHPUnit_Framework_Test $teste, Exception $e, $tempo)
{
printf("O Teste '%s' está incompleto.\n", $teste->getName());
}

public function addSkippedTest(PHPUnit_Framework_Test $teste, Exception $e, $tempo)
{
printf("O Teste '%s' foi pulado.\n", $teste->getName());
}

public function startTest(PHPUnit_Framework_Test $teste)
{
printf("O Teste '%s' iniciou.\n", $teste->getName());
}

public function endTest(PHPUnit_Framework_Test $teste, $tempo)
{
printf("O Teste '%s' terminou.\n", $teste->getName());
}

public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
{
printf("A Suíte de Testes '%s' iniciou.\n", $suite->getName());
}

public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
{
printf("A Suíte de Testes '%s' terminou.\n", $suite->getName());
}
}
?>

Em “Ouvintes de Teste” você pode ver como configurar o PHPUnit para anexar seu ouvinte de teste para a execução do teste.

Subclasse PHPUnit_Extensions_TestDecorator

ocê pode envolver casos de teste ou suítes de teste em uma subclasse do PHPUnit_Extensions_TestDecorator e usar o padrão de design do Decorador para realizar algumas ações antes e depois da execução do teste.

O PHPUnit navega com dois decoradores de teste concretos: PHPUnit_Extensions_RepeatedTest e PHPUnit_Extensions_TestSetup. O formador é usado para executar um teste repetidamente e apenas o conta como bem-sucedido se todas as iterações forem bem-sucedidas. O último foi discutido em Capítulo 6.

Exemplo 19.4 mostra uma versão resumida do decorador de teste PHPUnit_Extensions_RepeatedTest que ilustra como escrever seus próprios decoradores de teste.

Exemplo 19.4: O Decorador RepeatedTest

<?php
require_once 'PHPUnit/Extensions/TestDecorator.php';

class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator
{
private $repetirVezes = 1;

public function __construct(PHPUnit_Framework_Test $teste, $repetirVezes = 1)
{
parent::__construct($teste);

if (is_integer($repetirVezes) &&
$repetirVezes >= 0) {
$this->repetirVezes = $repetirVezes;
}
}

public function count()
{
return $this->repetirVezes * $this->teste->count();
}

public function run(PHPUnit_Framework_TestResult $resultado = NULL)
{
if ($resultado === NULL) {
$resultado = $this->createResult();
}

for ($i = 0; $i < $this->repetirVezes && !$resultado->shouldStop(); $i++) {
$this->teste->run($resultado);
}

return $resultado;
}
}
?>

Implementando PHPUnit_Framework_Test

A interface PHPUnit_Framework_Test é limitada e fácil de implementar. Você pode escrever uma implementação do PHPUnit_Framework_Test que é mais simples que PHPUnit_Framework_TestCase e que executa data-driven tests, por exemplo.

Exemplo 19.5 mostra uma classe de caso de teste guiado por dados que compara valores de um arquivo com Valores Separados por Vírgulas (CSV). Cada linha de tal arquivo parece com foo;bar, onde o primeiro valor é o qual esperamos e o segundo é o real.

Exemplo 19.5: Um teste guiado por dados

<?php
class GuiadoPorDadosTest implements PHPUnit_Framework_Test
{
private $linhas;

public function __construct($arquivoDados)
{
$this->linhas = file($arquivoDados);
}

public function count()
{
return 1;
}

public function run(PHPUnit_Framework_TestResult $resultado = NULL)
{
if ($resultado === NULL) {
$resultado = new PHPUnit_Framework_TestResult;
}

foreach ($this->linhas as $linha) {
$resultado->startTest($this);
PHP_Timer::start();
$stopTime = NULL;

list($esperado, $real) = explode(';', $linha);

try {
PHPUnit_Framework_Assert::assertEquals(
trim($esperado), trim($real)
);
}

catch (PHPUnit_Framework_AssertionFailedError $e) {
$stopTime = PHP_Timer::stop();
$resultado->addFailure($this, $e, $stopTime);
}

catch (Exception $e) {
$stopTime = PHP_Timer::stop();
$resultado->addError($this, $e, $stopTime);
}

if ($stopTime === NULL) {
$stopTime = PHP_Timer::stop();
}

$resultado->endTest($this, $stopTime);
}

return $resultado;
}
}

$teste = new GuiadoPorDadosTest('arquivo_dados.csv');
$resultado = PHPUnit_TextUI_TestRunner::run($teste);
?>
PHPUnit 3.7.0 by Sebastian Bergmann.

.F

Time: 0 seconds

There was 1 failure:

1) GuiadoPorDadosTest
Failed asserting that two strings are equal.
expected string <bar>
difference < x>
got string <baz>
/home/sb/GuiadoPorDadosTest.php:32
/home/sb/GuiadoPorDadosTest.php:53

FAILURES!
Tests: 2, Failures: 1.

Prev Next
1. Automatizando Testes
2. Objetivos do PHPUnit
3. Instalando o PHPUnit
PEAR
Composer
PHP Archive (PHAR)
Pacotes opcionais
Atualizando
4. Escrevendo Testes para o PHPUnit
Dependências de Testes
Provedores de Dados
Testando Exceções
Testando Erros PHP
Testando Saídas
Asserções
assertArrayHasKey()
assertClassHasAttribute()
assertClassHasStaticAttribute()
assertContains()
assertContainsOnly()
assertContainsOnlyInstancesOf()
assertCount()
assertEmpty()
assertEqualXMLStructure()
assertEquals()
assertFalse()
assertFileEquals()
assertFileExists()
assertGreaterThan()
assertGreaterThanOrEqual()
assertInstanceOf()
assertInternalType()
assertJsonFileEqualsJsonFile()
assertJsonStringEqualsJsonFile()
assertJsonStringEqualsJsonString()
assertLessThan()
assertLessThanOrEqual()
assertNull()
assertObjectHasAttribute()
assertRegExp()
assertStringMatchesFormat()
assertStringMatchesFormatFile()
assertSame()
assertSelectCount()
assertSelectEquals()
assertSelectRegExp()
assertStringEndsWith()
assertStringEqualsFile()
assertStringStartsWith()
assertTag()
assertThat()
assertTrue()
assertXmlFileEqualsXmlFile()
assertXmlStringEqualsXmlFile()
assertXmlStringEqualsXmlString()
Saída de Erro
Casos Extremos
5. O executor de testes em linha-de-comando
Comutadores de linha-de-comando
6. Ambientes
Mais setUp() que tearDown()
Variantes
Compartilhando Ambientes
Estado Global
7. Organizando Testes
Compondo uma Suíte de Testes usando o Sistema de Arquivos
Compondo uma Suíte de Testes Usando uma Configuração XML
8. Testando Bancos de Dados
Fornecedores Suportados para Testes de Banco de Dados
Dificuldades em Testes de Bancos de Dados
Os quatro estágios dos testes com banco de dados
1. Limpar o Banco de Dados
2. Configurar o ambiente
3–5. Executar Teste, Verificar saída e Teardown
Configuração de Caso de Teste de Banco de Dados do PHPUnit
Implementando getConnection()
Implementando getDataSet()
E quanto ao Esquema do Banco de Dados (DDL)?
Dica: Use seu próprio Caso Abstrato de Teste de Banco de Dados
Entendendo Conjunto de Dados e Tabelas de Dados
Implementações disponíveis
Cuidado com Chaves Estrangeiras
Implementando seus próprios Conjuntos de Dados/ Tabelas de Dados
A API de Conexão
API de Asserções de Banco de Dados
Assertando a contagem de linhas de uma Tabela
Assertando o Estado de uma Tabela
Assertando o Resultado de uma Query
Assertando o Estado de Múltiplas Tabelas
Perguntas Mais Frequentes
O PHPUnit vai (re)criar o esquema do banco de dados para cada teste?
Sou forçado a usar PDO em minha aplicação para que a Extensão para Banco de Dados funcione?
O que posso fazer quando recebo um Erro Too much Connections?
Como lidar com NULL usando Conjuntos de Dados XML Plano / CSV?
9. Testes Incompletos e Pulados
Testes Incompletos
Pulando Testes
Pulando Testes usando @requires
10. Dublês de Testes
Esboços (stubs)
Objetos Falsos
Esboçando e Falsificando Serviços Web
Esboçando o Sistema de Arquivos
11. Práticas de Teste
Durante o Desenvolvimento
Durante a Depuração
12. Desenvolvimento Guiado por Testes
Exemplo da Conta Bancária
13. Desenvolvimento Guiado por Comportamento
Exemplo do Jogo de Boliche
14. Análise de Cobertura de Código
Especificando métodos cobertos
Ignorando Blocos de Código
Incluindo e Excluindo Arquivos
Casos Extremos
15. Outros Usos para Testes
Documentação Ágil
Testes Inter-Equipes
16. Gerador de Esqueleto
Gerando um Esqueleto de Classe de Caso de Teste
Gerando uma Classe Esqueleto de uma Classe de Caso de Teste
17. PHPUnit e Selenium
Servidor Selenium
Instalação
PHPUnit_Extensions_Selenium2TestCase
PHPUnit_Extensions_SeleniumTestCase
18. Registrando
Resultados de Teste (XML)
Resultados de Teste (TAP)
Resultados de Teste (JSON)
Cobertura de Código (XML)
Cobertura de Código (TEXTO)
19. Estendendo o PHPUnit
Subclasse PHPUnit_Framework_TestCase
Escreva asserções personalizadas
Implementando PHPUnit_Framework_TestListener
Subclasse PHPUnit_Extensions_TestDecorator
Implementando PHPUnit_Framework_Test
A. Assertions
B. Anotações
@author
@backupGlobals
@backupStaticAttributes
@codeCoverageIgnore*
@covers
@coversNothing
@dataProvider
@depends
@expectedException
@expectedExceptionCode
@expectedExceptionMessage
@group
@outputBuffering
@requires
@runTestsInSeparateProcesses
@runInSeparateProcess
@test
@testdox
@ticket
C. O arquivo de configuração XML
PHPUnit
Suítes de Teste
Grupos
Incluindo e Excluindo Arquivos para Cobertura de Código
Registrando
Ouvintes de Teste
Setting PHP INI settings, Constants and Global Variables
Configurando Navegadores para Selenium RC
D. Índice
E. Bibliografia
F. Copyright