<?xml version="1.0" encoding="utf-8" ?>

<chapter id="extending-phpunit">
  <title>Estendendo o PHPUnit</title>

  <para>
    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.
  </para>

  <section id="extending-phpunit.PHPUnit_Framework_TestCase">
    <title>Subclasse PHPUnit_Framework_TestCase</title>

    <para>
      <indexterm><primary>PHPUnit_Framework_TestCase</primary></indexterm>

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

  <section id="extending-phpunit.custom-assertions">
    <title>Escreva asserções personalizadas</title>

    <para>
      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
      <xref linkend="extending-phpunit.examples.Assert.php"/>, o método
      <literal>assertTrue()</literal> é apenas um empacotador em torno dos métodos
      <literal>isTrue()</literal> e <literal>assertThat()</literal>:
      <literal>isTrue()</literal> cria um objeto comparador que é passado para
      <literal>assertThat()</literal> para avaliação.
    </para>

    <example id="extending-phpunit.examples.Assert.php">
      <title>Os métodos assertTrue() e isTrue() da classe PHPUnitFramework_Assert</title>
      <programlisting><![CDATA[<?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;
}

// ...
}?>]]></programlisting>
    </example>

    <para>
      <xref linkend="extending-phpunit.examples.IsTrue.php"/> mostra como
      <literal>PHPUnit_Framework_Constraint_IsTrue</literal> estende a classe base abstrata para objetos comparadores (ou restritores),
      <literal>PHPUnit_Framework_Constraint</literal>.
    </para>

    <example id="extending-phpunit.examples.IsTrue.php">
      <title>A classe PHPUnit_Framework_Constraint_IsTrue</title>
      <programlisting><![CDATA[<?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';
}
}?>]]></programlisting>
    </example>

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

  <section id="extending-phpunit.PHPUnit_Framework_TestListener">
    <title>Implementando PHPUnit_Framework_TestListener</title>

    <para>
      <indexterm><primary>PHPUnit_Framework_TestListener</primary></indexterm>

      <xref linkend="extending-phpunit.examples.SimpleTestListener.php" />
      mostra uma implementação simples da interface <literal>PHPUnit_Framework_TestListener</literal>.
    </para>

    <example id="extending-phpunit.examples.SimpleTestListener.php">
      <title>Um simples ouvinte de teste</title>
      <programlisting><![CDATA[<?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 addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
{
printf("O Teste '%s' é arriscado.\n", $test->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());
}
}
?>]]></programlisting>
    </example>

    <para>
      Em <xref linkend="appendixes.configuration.test-listeners"/> você pode ver como configurar o PHPUnit para anexar seu ouvinte de teste para a execução do teste.
    </para>
  </section>

  <section id="extending-phpunit.PHPUnit_Extensions_TestDecorator">
    <title>Subclasse PHPUnit_Extensions_TestDecorator</title>

    <para>
      <indexterm><primary>PHPUnit_Extensions_TestDecorator</primary></indexterm>

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

    <para>
      <indexterm><primary>PHPUnit_Extensions_RepeatedTest</primary></indexterm>
      <indexterm><primary>PHPUnit_Extensions_TestSetup</primary></indexterm>

      O PHPUnit navega com dois decoradores de teste concretos:
      <literal>PHPUnit_Extensions_RepeatedTest</literal> e
      <literal>PHPUnit_Extensions_TestSetup</literal>. 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 <xref linkend="fixtures" />.
    </para>

    <para>
      <xref linkend="extending-phpunit.examples.RepeatedTest.php" />
      mostra uma versão resumida do decorador de teste <literal>PHPUnit_Extensions_RepeatedTest</literal>
      que ilustra como escrever seus próprios decoradores de teste.
    </para>

    <example id="extending-phpunit.examples.RepeatedTest.php">
      <title>O Decorador RepeatedTest</title>
      <programlisting><![CDATA[<?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;
}
}
?>]]></programlisting>
    </example>
  </section>

  <section id="extending-phpunit.PHPUnit_Framework_Test">
    <title>Implementando PHPUnit_Framework_Test</title>

    <para>
      <indexterm><primary>PHPUnit_Framework_Test</primary></indexterm>
      <indexterm><primary>Testes Guiados por Dados</primary></indexterm>

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

    <para>
      <xref linkend="extending-phpunit.examples.DataDrivenTest.php" />
      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
      <literal>foo;bar</literal>, onde o primeiro valor é o qual esperamos e o segundo é o real.
    </para>

    <example id="extending-phpunit.examples.DataDrivenTest.php">
      <title>Um teste guiado por dados</title>
      <programlisting><![CDATA[<?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);
?>]]></programlisting>

      <screen><![CDATA[PHPUnit 4.0.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.]]></screen>
    </example>
  </section>
</chapter>
