text: <?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>
  