Manual do PHPUnit

Sebastian Bergmann

Este material é licenciado sob a Licença Não-Adaptada Creative Commons Attribution 3.0.

Edição para PHPUnit 5.7. Atualizado em 2016-12-05.


1. Instalando PHPUnit
Requerimentos
PHP Archive (PHAR)
Windows
Verificando lançamentos do PHAR PHPUnit
Composer
Pacotes adicionais
2. Escrevendo Testes para o PHPUnit
Dependências de Testes
Provedores de Dados
Testando Exceções
Testando Erros PHP
Testando Saídas
Saída de Erro
Casos Extremos
3. O executor de testes em linha-de-comando
Opções de linha-de-comando
4. Ambientes
Mais setUp() que tearDown()
Variações
Compartilhando Ambientes
Estado Global
5. Organizando Testes
Compondo uma Suíte de Testes usando o Sistema de Arquivos
Compondo uma Suíte de Testes Usando uma Configuração XML
6. Testes arriscados
Testes Inúteis
Cobertura de Código Involuntária
Saída Durante a Execução de Teste
Tempo de Espera de Execução de Teste
Manipulação do Estado Global
7. Testes Incompletos e Pulados
Testes Incompletos
Pulando Testes
Pulando Testes usando @requires
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 resultado e Desmontar (Teardown)
Configuração de um 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
Asseverado a contagem de linhas de uma Tabela
Asseverando o Estado de uma Tabela
Asseverando o Resultado de uma Query
Asseverando 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. Dublês de Testes
Esboços (stubs)
Objetos Falsos
Profecia
Falsificando Traits e Classes Abstratas
Esboçando e Falsificando Serviços Web
Esboçando o Sistema de Arquivos
10. Práticas de Teste
Durante o Desenvolvimento
Durante a Depuração
11. Análise de Cobertura de Código
Métricas de Software para Cobertura de Código
Incluindo e Excluindo Arquivos
Ignorando Blocos de Código
Especificando métodos cobertos
Casos Extremos
12. Outros Usos para Testes
Documentação Ágil
Testes Inter-Equipes
13. PHPUnit e Selenium
Servidor Selenium
Instalação
PHPUnit_Extensions_Selenium2TestCase
PHPUnit_Extensions_SeleniumTestCase
14. Registrando
Resultados de Teste (XML)
Resultados de Teste (TAP)
Resultados de Teste (JSON)
Cobertura de Código (XML)
Cobertura de Código (TEXT)
15. Estendendo o PHPUnit
Subclasse PHPUnit_Framework_TestCase
Escreva asserções personalizadas
Implementando PHPUnit_Framework_TestListener
Subclasse PHPUnit_Extensions_TestDecorator
Implementando PHPUnit_Framework_Test
A. Asserções
assertArrayHasKey()
assertClassHasAttribute()
assertArraySubset()
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()
assertStringEndsWith()
assertStringEqualsFile()
assertStringStartsWith()
assertThat()
assertTrue()
assertXmlFileEqualsXmlFile()
assertXmlStringEqualsXmlFile()
assertXmlStringEqualsXmlString()
B. Anotações
@author
@after
@afterClass
@backupGlobals
@backupStaticAttributes
@before
@beforeClass
@codeCoverageIgnore*
@covers
@coversDefaultClass
@coversNothing
@dataProvider
@depends
@expectedException
@expectedExceptionCode
@expectedExceptionMessage
@expectedExceptionMessageRegExp
@group
@large
@medium
@preserveGlobalState
@requires
@runTestsInSeparateProcesses
@runInSeparateProcess
@small
@test
@testdox
@ticket
@uses
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
Definindo configurações PHP INI, Constantes e Variáveis Globais
Configurando Navegadores para Selenium RC
D. Atualizando
Atualizando de PHPUnit 3.7 para PHPUnit 4.0
E. Índice
F. Bibliografia
G. Direitos autorais

Capítulo 1. Instalando PHPUnit

Requerimentos

PHPUnit 5.7 requer PHP 5.3.3; Usar a última versão do PHP é altamente recomendável.

PHPUnit requer as extensões dom e json, que são normalmente habilitadas por padrão.

PHPUnit também requer as extensões pcre, reflection e spl. Elas são requeridas pelo núcleo do PHP desde 5.3.0 e normalmente não podem ser desabilitadas.

A funcionalidade de relatório de cobertura de código requer as extensões Xdebug (2.1.3 ou superior) e tokenizer. Geração de relatórios XML requer a extensão xmlwriter.

PHP Archive (PHAR)

A maneira mais fácil de obter o PHPUnit é baixar um PHP Archive (PHAR) que tem todas dependências requeridas (assim como algumas opcionais) do PHPUnit empacotados em um único arquivo.

A extensão phar é requerida para usar PHP Archives (PHAR).

A extensão openssl é requerida para usar a funcionalidade --self-update do PHAR.

Se a extensão Suhosin está habilitada, você precisa permitir a execução dos PHARs em seu php.ini:

suhosin.executor.include.whitelist = phar

Nota

Para baixar de https://phar.phpunit.de/ você precisa de um cliente com suporte a TLS/SNI; e.g., wget 1.14 (ou superior).

Para instalar globalmente o PHAR:

$ wget https://phar.phpunit.de/phpunit.phar
$ chmod +x phpunit.phar
$ sudo mv phpunit.phar /usr/local/bin/phpunit
$ phpunit --version
PHPUnit x.y.z by Sebastian Bergmann and contributors.

Você pode também usar o arquivo PHAR baixado diretamente:

$ wget https://phar.phpunit.de/phpunit.phar
$ php phpunit.phar --version
PHPUnit x.y.z by Sebastian Bergmann and contributors.

Windows

Instalar globalmente o PHAR envolve o mesmo procedimento de instalar manualmente o Composer no Windows:

  1. Crie um diretório para os binários PHP; e.g., C:\bin

  2. Acrescente ;C:\bin à sua variável de ambiente PATH (ajuda relacionada)

  3. Baixe https://phar.phpunit.de/phpunit.phar e salve o arquivo como C:\bin\phpunit.phar

  4. Abra uma linha de comando (e.g., pressione Windows+R » digite cmd » ENTER)

  5. Crie um script de envoltório batch (resulta em C:\bin\phpunit.cmd):

    C:\Users\username> cd C:\bin
    C:\bin> echo @php "%~dp0phpunit.phar" %* > phpunit.cmd
    C:\bin> exit
    
  6. Abra uma nova linha de comando e confirme que você pode executar PHPUnit de qualquer caminho:

    C:\Users\username> phpunit --version
    PHPUnit x.y.z by Sebastian Bergmann and contributors.
    

Para ambientes shell Cygwin e/ou MingW32 (e.g., TortoiseGit), você pode pular o passo 5 acima, simplesmente salve o arquivo como phpunit (sem a extensão .phar), e torne-o executável via chmod 775 phpunit.

Verificando lançamentos do PHAR PHPUnit

Todos lançamentos oficiais do código distribuído pelo Projeto PHPUnit são assinados pelo gerenciador de lançamentos para o lançamento. Assinaturas PGP e hashes SHA1 estão disponíveis para verificação no phar.phpunit.de.

O exemplo a seguir detalha como a verificação de lançamento funciona. Começamos baixando phpunit.phar bem como sua assinatura PGP independente phpunit.phar.asc:

wget https://phar.phpunit.de/phpunit.phar
wget https://phar.phpunit.de/phpunit.phar.asc

Queremos verificar o PHP Archive do PHPUnit (phpunit.phar) contra sua assinatura independente (phpunit.phar.asc):

gpg phpunit.phar.asc
gpg: Signature made Sat 19 Jul 2014 01:28:02 PM CEST using RSA key ID 6372C20A
gpg: Can't check signature: public key not found

Nós não temos a chave pública do gerenciador de lançamento (6372C20A) no próprio sistema local. A fim de prosseguir com a verificação nós precisamos recuperar a chave pública do gerenciador de lançamentos a partir de um servidor de chaves. Um tal servidor é pgp.uni-mainz.de. Os servidores de chaves públicas são conectados entre si, então você deve ser capaz de se conectar a qualquer servidor de chaves.

gpg --keyserver pgp.uni-mainz.de --recv-keys 0x4AA394086372C20A
gpg: requesting key 6372C20A from hkp server pgp.uni-mainz.de
gpg: key 6372C20A: public key "Sebastian Bergmann <sb@sebastian-bergmann.de>" imported
gpg: Total number processed: 1
gpg:               imported: 1  (RSA: 1)

Agora recebemos uma chave pública para uma entidade conhecida como "Sebastian Bergmann <sb@sebastian-bergmann.de>". Porém, nós não temos nenhuma maneira de verificar que essa foi criada pela pessoa conhecida como Sebastian Bergmann. Mas, vamos tentar verificar a assinatura de lançamento novamente.

gpg phpunit.phar.asc
gpg: Signature made Sat 19 Jul 2014 01:28:02 PM CEST using RSA key ID 6372C20A
gpg: Good signature from "Sebastian Bergmann <sb@sebastian-bergmann.de>"
gpg:                 aka "Sebastian Bergmann <sebastian@php.net>"
gpg:                 aka "Sebastian Bergmann <sebastian@thephp.cc>"
gpg:                 aka "Sebastian Bergmann <sebastian@phpunit.de>"
gpg:                 aka "Sebastian Bergmann <sebastian.bergmann@thephp.cc>"
gpg:                 aka "[jpeg image of size 40635]"
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: D840 6D0D 8294 7747 2937  7831 4AA3 9408 6372 C20A

Neste ponto, a assinatura é boa, mas não confiamos nesta chave. Uma boa assinatura significa que o arquivo não foi adulterado. Porém, devido a natureza da criptografia de chave pública, você precisa adicionalmente verificar que a chave 6372C20A foi criada pelo verdadeiro Sebastian Bergmann.

Qualquer invasor pode criar uma chave pública e enviá-la para os servidores de chave pública. Eles podem, então, criar um lançamento malicioso assinado pela chave fake. Então, se você tentar verificar a assinatura desse lançamento corrompido, terá sucesso por quê a chave não é a chave "verdadeira". Portanto, você precisa validar o autenticidade dessa chave. Validar a autenticidade da chave pública, no entanto, está fora do escopo desta documentação.

Pode ser prudente criar um script shell para gerenciar a instalação do PHPUnit que verifica a assinatura GnuPG antes de rodar sua suíte de teste. Por exemplo:

#!/usr/bin/env bash
clean=1 # Delete phpunit.phar after the tests are complete?
aftercmd="php phpunit.phar --bootstrap bootstrap.php src/tests"
gpg --fingerprint D8406D0D82947747293778314AA394086372C20A
if [ $? -ne 0 ]; then
    echo -e "\033[33mDownloading PGP Public Key...\033[0m"
    gpg --recv-keys D8406D0D82947747293778314AA394086372C20A
    # Sebastian Bergmann <sb@sebastian-bergmann.de>
    gpg --fingerprint D8406D0D82947747293778314AA394086372C20A
    if [ $? -ne 0 ]; then
        echo -e "\033[31mCould not download PGP public key for verification\033[0m"
        exit
    fi
fi

if [ "$clean" -eq 1 ]; then
    # Let's clean them up, if they exist
    if [ -f phpunit.phar ]; then
        rm -f phpunit.phar
    fi
    if [ -f phpunit.phar.asc ]; then
        rm -f phpunit.phar.asc
    fi
fi

# Let's grab the latest release and its signature
if [ ! -f phpunit.phar ]; then
    wget https://phar.phpunit.de/phpunit.phar
fi
if [ ! -f phpunit.phar.asc ]; then
    wget https://phar.phpunit.de/phpunit.phar.asc
fi

# Verify before running
gpg --verify phpunit.phar.asc phpunit.phar
if [ $? -eq 0 ]; then
    echo
    echo -e "\033[33mBegin Unit Testing\033[0m"
    # Run the testing suite
    `$after_cmd`
    # Cleanup
    if [ "$clean" -eq 1 ]; then
        echo -e "\033[32mCleaning Up!\033[0m"
        rm -f phpunit.phar
        rm -f phpunit.phar.asc
    fi
else
    echo
    chmod -x phpunit.phar
    mv phpunit.phar /tmp/bad-phpunit.phar
    mv phpunit.phar.asc /tmp/bad-phpunit.phar.asc
    echo -e "\033[31mSignature did not match! PHPUnit has been moved to /tmp/bad-phpunit.phar\033[0m"
    exit 1
fi
      

Composer

Simplesmente adicione uma dependência phpunit/phpunit ao arquivo composer.json do projeto se você usa o Composer para gerenciar as dependências do seu projeto. Aqui está um exemplo mínimo de um arquivo composer.json que define apenas uma dependência em desenvolvimento do PHPUnit 5.7:

{
    "require-dev": {
        "phpunit/phpunit": "5.5.*"
    }
}

Para uma instalação em todo o sistema, você pode rodar:

composer global require "phpunit/phpunit=5.5.*"

Certifique-se de que você tem ~/.composer/vendor/bin/ em seu path.

Pacotes adicionais

Os seguintes pacotes opcionais estão disponíveis:

PHP_Invoker

A classe utilitária para invocar callables com um tempo limite. Este pacote é requerido para impor limites de tempo de teste no modo estrito.

Este pacote está incluso na distribuição PHAR do PHPUnit. Ele pode ser instalado via Composer adicionando a seguinte dependência "require-dev":

"phpunit/php-invoker": "*"
DbUnit

DbUnit porta para PHP/PHPUnit suportar teste de interação de banco de dados.

Este pacote está incluso na distribuição PHAR do PHPUnit. Ele pode ser instalado via Composer adicionando a seguinte dependência "require-dev":

"phpunit/dbunit": ">=1.2"
PHPUnit_Selenium

Integração Selenium RC para PHPUnit.

Este pacote está incluso na distribuição PHAR do PHPUnit. Ele pode ser instalado via Composer adicionando a seguinte dependência "require-dev":

"phpunit/phpunit-selenium": ">=1.2"

Capítulo 2. Escrevendo Testes para o PHPUnit

Exemplo 2.1 mostra como podemos escrever testes usando o PHPUnit que exercita operações de vetor do PHP. O exemplo introduz as convenções básicas e passos para escrever testes com o PHPUnit:

  1. Os testes para uma classe Classe vão dentro de uma classe ClasseTest.

  2. ClasseTest herda (na maioria das vezes) de PHPUnit_Framework_TestCase.

  3. Os testes são métodos públicos que são nomeados test*.

    Alternativamente, você pode usar a anotação @test em um bloco de documentação de um método para marcá-lo como um método de teste.

  4. Dentro dos métodos de teste, métodos de confirmação como assertEquals() (veja Apêndice A) são usados para confirmar que um valor real equivale a um valor esperado.

Exemplo 2.1: Testando operações de vetores com o PHPUnit

<?php
class StackTest extends PHPUnit_Framework_TestCase
{
    public function testPushAndPop()
    {
        $stack = array();
        $this->assertEquals(0, count($stack));

        array_push($stack, 'foo');
        $this->assertEquals('foo', $stack[count($stack)-1]);
        $this->assertEquals(1, count($stack));

        $this->assertEquals('foo', array_pop($stack));
        $this->assertEquals(0, count($stack));
    }
}
?>

 

Sempre que você estiver tentado a escrever algo em uma declaração print ou uma expressão depuradora, escreva como um teste em vez disso.

 
 --Martin Fowler

Dependências de Testes

 

Testes Unitários são primeiramente escritos como uma boa prática para ajudar desenvolvedores a identificar e corrigir defeitos, refatorar o código e servir como documentação para uma unidade de programa sob teste. Para conseguir esses benefícios, testes unitários idealmente deveriam cobrir todos os caminhos possíveis em um programa. Um teste unitário geralmente cobre um caminho específico em uma função ou método. Porém um método de teste não é necessariamente uma entidade encapsulada e independente. Às vezes existem dependências implícitas entre métodos de teste, escondidas no cenário de implementação de um teste.

 
 --Adrian Kuhn et. al.

O PHPUnit suporta a declaração explícita de dependências entre métodos de teste. Tais dependências não definem a ordem em que os métodos de teste devem ser executados, mas permitem o retorno de uma instância do ambiente do teste por um produtor e a passagem dele para os consumidores dependentes.

  • Um produtor é um método de teste que produz a sua unidade sob teste como valor de retorno.

  • Um consumidor é um método de teste que depende de um ou mais produtores e seus valores retornados.

Exemplo 2.2 mostra como usar a anotação @depends para expressar dependências entre métodos de teste.

Exemplo 2.2: Usando a anotação @depends para expressar dependências

<?php
class StackTest extends PHPUnit_Framework_TestCase
{
    public function testEmpty()
    {
        $stack = array();
        $this->assertEmpty($stack);

        return $stack;
    }

    /**
     * @depends testEmpty
     */
    public function testPush(array $stack)
    {
        array_push($stack, 'foo');
        $this->assertEquals('foo', $stack[count($stack)-1]);
        $this->assertNotEmpty($stack);

        return $stack;
    }

    /**
     * @depends testPush
     */
    public function testPop(array $stack)
    {
        $this->assertEquals('foo', array_pop($stack));
        $this->assertEmpty($stack);
    }
}
?>

No exemplo acima, o primeiro teste, testEmpty(), cria um novo vetor e assegura que o mesmo é vazio. O teste então retorna o ambiente como resultado. O segundo teste, testPush(), depende de testEmpty() e lhe é passado o resultado do qual ele depende como um argumento. Finalmente, testPop() depende de testPush().

Para localizar defeitos rapidamente, queremos nossa atenção focada nas falhas relevantes dos testes. É por isso que o PHPUnit pula a execução de um teste quando um teste do qual ele depende falha. Isso melhora a localização de defeitos por explorar as dependências entre os testes como mostrado em Exemplo 2.3.

Exemplo 2.3: Explorando as dependências entre os testes

<?php
class DependencyFailureTest extends PHPUnit_Framework_TestCase
{
    public function testOne()
    {
        $this->assertTrue(FALSE);
    }

    /**
     * @depends testOne
     */
    public function testTwo()
    {
    }
}
?>
phpunit --verbose DependencyFailureTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

FS

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) DependencyFailureTest::testOne
Failed asserting that false is true.

/home/sb/DependencyFailureTest.php:6

There was 1 skipped test:

1) DependencyFailureTest::testTwo
This test depends on "DependencyFailureTest::testOne" to pass.


FAILURES!
Tests: 1, Assertions: 1, Failures: 1, Skipped: 1.

Um teste pode ter mais de uma anotação @depends. O PHPUnit não muda a ordem em que os testes são executados, portanto você deve se certificar de que as dependências de um teste podem realmente ser encontradas antes de executar o teste.

Um teste que tem mais de uma anotação @depends vai obter um ambiente a partir do primeiro produtor como o primeiro argumento, um ambiente a partir do segundo produtor como o segundo argumento, e assim por diante. Veja Exemplo 2.4

Exemplo 2.4: Teste com múltiplas dependências

<?php
class MultipleDependenciesTest extends PHPUnit_Framework_TestCase
{
    public function testProducerFirst()
    {
        $this->assertTrue(true);
        return 'first';
    }

    public function testProducerSecond()
    {
        $this->assertTrue(true);
        return 'second';
    }

    /**
     * @depends testProducerFirst
     * @depends testProducerSecond
     */
    public function testConsumer()
    {
        $this->assertEquals(
            array('first', 'second'),
            func_get_args()
        );
    }
}
?>
phpunit --verbose MultipleDependenciesTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

...

Time: 0 seconds, Memory: 3.25Mb

OK (3 tests, 3 assertions)

Provedores de Dados

Um método de teste pode aceitar argumentos arbitrários. Esses argumentos devem ser fornecidos por um método provedor de dados (additionProvider() em Exemplo 2.5). O método provedor de dados a ser usado é especificado usando a anotação @dataProvider.

Um método provedor de dados deve ser public e ou retornar um vetor de vetores ou um objeto que implementa a interface Iterator e produz um vetor para cada passo da iteração. Para cada vetor que é parte da coleção o método de teste será chamado com os conteúdos do vetor como seus argumentos.

Exemplo 2.5: Usando um provedor de dados que retorna um vetor de vetores

<?php
class DataTest extends PHPUnit_Framework_TestCase
{
    /**
     * @dataProvider additionProvider
     */
    public function testAdd($a, $b, $expected)
    {
        $this->assertEquals($expected, $a + $b);
    }

    public function additionProvider()
    {
        return array(
          array(0, 0, 0),
          array(0, 1, 1),
          array(1, 0, 1),
          array(1, 1, 3)
        );
    }
}
?>
phpunit DataTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

...F

Time: 0 seconds, Memory: 5.75Mb

There was 1 failure:

1) DataTest::testAdd with data set #3 (1, 1, 3)
Failed asserting that 2 matches expected 3.

/home/sb/DataTest.php:9

FAILURES!
Tests: 4, Assertions: 4, Failures: 1.

Exemplo 2.6: Usando um provedor de dados que retorna um objeto Iterador

<?php
require 'CsvFileIterator.php';

class DataTest extends PHPUnit_Framework_TestCase
{
    /**
     * @dataProvider additionProvider
     */
    public function testAdd($a, $b, $expected)
    {
        $this->assertEquals($expected, $a + $b);
    }

    public function additionProvider()
    {
        return new CsvFileIterator('data.csv');
    }
}
?>
phpunit DataTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

...F

Time: 0 seconds, Memory: 5.75Mb

There was 1 failure:

1) DataTest::testAdd with data set #3 ('1', '1', '3')
Failed asserting that 2 matches expected '3'.

/home/sb/DataTest.php:11

FAILURES!
Tests: 4, Assertions: 4, Failures: 1.

Exemplo 2.7: A classe CsvFileIterator

<?php
class CsvFileIterator implements Iterator {
    protected $file;
    protected $key = 0;
    protected $current;

    public function __construct($file) {
        $this->file = fopen($file, 'r');
    }

    public function __destruct() {
        fclose($this->file);
    }

    public function rewind() {
        rewind($this->file);
        $this->current = fgetcsv($this->file);
        $this->key = 0;
    }

    public function valid() {
        return !feof($this->file);
    }

    public function key() {
        return $this->key;
    }

    public function current() {
        return $this->current;
    }

    public function next() {
        $this->current = fgetcsv($this->file);
        $this->key++;
    }
}
?>

Quando um teste recebe uma entrada tanto de um método @dataProvider quanto de um ou mais testes dos quais ele @depends, os argumentos do provedor de dados virão antes daqueles dos quais ele é dependente. Os argumentos dos quais o teste depende serão os mesmos para cada conjunto de dados. Veja Exemplo 2.8

Exemplo 2.8: Combinação de @depends e @dataProvider no mesmo teste

<?php
class DependencyAndDataProviderComboTest extends PHPUnit_Framework_TestCase
{
    public function provider()
    {
        return array(array('provider1'), array('provider2'));
    }

    public function testProducerFirst()
    {
        $this->assertTrue(true);
        return 'first';
    }

    public function testProducerSecond()
    {
        $this->assertTrue(true);
        return 'second';
    }

    /**
     * @depends testProducerFirst
     * @depends testProducerSecond
     * @dataProvider provider
     */
    public function testConsumer()
    {
        $this->assertEquals(
            array('provider1', 'first', 'second'),
            func_get_args()
        );
    }
}
?>
phpunit --verbose DependencyAndDataProviderComboTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

...F

Time: 0 seconds, Memory: 3.50Mb

There was 1 failure:

1) DependencyAndDataProviderComboTest::testConsumer with data set #1 ('provider2')
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
Array (
-    0 => 'provider1'
+    0 => 'provider2'
1 => 'first'
2 => 'second'
)

/home/sb/DependencyAndDataProviderComboTest.php:31

FAILURES!
Tests: 4, Assertions: 4, Failures: 1.

Nota

Quando um teste depende de um teste que usa provedores de dados, o teste dependente será executado quando o teste do qual ele depende for bem sucedido em pelo menos um conjunto de dados. O resultado de um teste que usa provedores de dados não pode ser injetado dentro de um teste dependente.

Nota

Todos provedores de dados são executados antes da chamada ao método estático setUpBeforeClass e a primeira chamada ao método setUp. Por isso você não pode acessar quaisquer variáveis que criar ali de dentro de um provedor de dados. Isto é necessário para que o PHPUnit seja capaz de calcular o número total de testes.

Testando Exceções

Exemplo 2.9 mostra como usar a anotação @expectedException para testar se uma exceção é lançada dentro do código de teste.

Exemplo 2.9: Usando a anotação @expectedException

<?php
class ExceptionTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException InvalidArgumentException
     */
    public function testException()
    {
    }
}
?>
phpunit ExceptionTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 4.75Mb

There was 1 failure:

1) ExceptionTest::testException
Expected exception InvalidArgumentException


FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Adicionalmente, você pode usar @expectedExceptionMessage, @expectedExceptionMessageRegExp e @expectedExceptionCode em combinação com @expectedException para testar a mensagem de exceção e o código de exceção como mostrado em Exemplo 2.10.

Exemplo 2.10: Usando as anotações @expectedExceptionMessage, @expectedExceptionMessageRegExp e @expectedExceptionCode

<?php
class ExceptionTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException        InvalidArgumentException
     * @expectedExceptionMessage Right Message
     */
    public function testExceptionHasRightMessage()
    {
        throw new InvalidArgumentException('Some Message', 10);
    }

    /**
     * @expectedException              InvalidArgumentException
     * @expectedExceptionMessageRegExp #Right.*#
     */
    public function testExceptionMessageMatchesRegExp()
    {
        throw new InvalidArgumentException('Some Message', 10);
    }

    /**
     * @expectedException     InvalidArgumentException
     * @expectedExceptionCode 20
     */
    public function testExceptionHasRightCode()
    {
        throw new InvalidArgumentException('Some Message', 10);
    }
}
?>
phpunit ExceptionTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

FFF

Time: 0 seconds, Memory: 3.00Mb

There were 3 failures:

1) ExceptionTest::testExceptionHasRightMessage
Failed asserting that exception message 'Some Message' contains 'Right Message'.

2) ExceptionTest::testExceptionMessageMatchesRegExp
Failed asserting that exception message 'Some Message' matches '#Right.*#'.

3) ExceptionTest::testExceptionHasRightCode
Failed asserting that expected exception code 20 is equal to 10.


FAILURES!
Tests: 3, Assertions: 6, Failures: 3.

Mais exemplos de @expectedExceptionMessage, @expectedExceptionMessageRegExp e @expectedExceptionCode são mostrados em “@expectedExceptionMessage”, “@expectedExceptionMessageRegExp” e “@expectedExceptionCode” respectivamente.

Alternativamente, você pode usar o método setExpectedException() ou setExpectedExceptionRegExp() para definir a exceção esperada como mostrado em Exemplo 2.11.

Exemplo 2.11: Esperando uma exceção surgir do código de teste

<?php
class ExceptionTest extends PHPUnit_Framework_TestCase
{
    public function testException()
    {
        $this->setExpectedException('InvalidArgumentException');
    }

    public function testExceptionHasRightMessage()
    {
        $this->setExpectedException(
          'InvalidArgumentException', 'Right Message'
        );
        throw new InvalidArgumentException('Some Message', 10);
    }

    public function testExceptionMessageMatchesRegExp()
    {
        $this->setExpectedExceptionRegExp(
          'InvalidArgumentException', '/Right.*/', 10
        );
        throw new InvalidArgumentException('The Wrong Message', 10);
    }

    public function testExceptionHasRightCode()
    {
        $this->setExpectedException(
          'InvalidArgumentException', 'Right Message', 20
        );
        throw new InvalidArgumentException('The Right Message', 10);
    }
}
?>
phpunit ExceptionTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

FFFF

Time: 0 seconds, Memory: 3.00Mb

There were 4 failures:

1) ExceptionTest::testException
Expected exception InvalidArgumentException

2) ExceptionTest::testExceptionHasRightMessage
Failed asserting that exception message 'Some Message' contains 'Right Message'.

3) ExceptionTest::testExceptionMessageMatchesRegExp
Failed asserting that exception message 'The Wrong Message' contains '/Right.*/'.

4) ExceptionTest::testExceptionHasRightCode
Failed asserting that expected exception code 20 is equal to 10.


FAILURES!
Tests: 4, Assertions: 8, Failures: 4.

Tabela 2.1 mostra os métodos fornecidos para testar exceções.

Tabela 2.1. Métodos para testar exceções

MétodoSignificado
void setExpectedException(string $nomeExcecao[, string $mensagemExcecao = '', inteiro $codigoExcecao = NULL])Define o $exceptionName, $exceptionMessage e $exceptionCode esperados.
void setExpectedExceptionRegExp(string $exceptionName[, string $exceptionMessageRegExp = '', integer $exceptionCode = NULL])Define o $exceptionName, $exceptionMessageRegExp e $exceptionCode esperados.
String getExpectedException()Retorna o nome da exceção esperada.

Você também pode usar a abordagem mostrada em Exemplo 2.12 para testar exceções

Exemplo 2.12: Abordagem alternativa para testar exceções

<?php
class ExceptionTest extends PHPUnit_Framework_TestCase {
    public function testException() {
        try {
            // ... Código que se espera que lance uma exceção ...
        }

        catch (InvalidArgumentException $expected) {
            return;
        }

        $this->fail('Uma exceção esperada não foi criada.');
    }
}
?>

Se o código que deve lançar uma exceção no Exemplo 2.12 não lançá-la, a chamada subsequente ao fail() vai parar o teste e sinalizar um problema com o teste. Se a exceção esperada é lançada, o bloco catch será executado, e o teste terminará com sucesso.

Testando Erros PHP

Por padrão, o PHPUnit converte os erros, avisos e notificações do PHP que são disparados durante a execução de um teste para uma exceção. Usando essas exceções, você pode, por exemplo, esperar que um teste dispare um erro PHP como mostrado no Exemplo 2.13.

Nota

A configuração em tempo de execução error_reporting do PHP pode limitar quais erros o PHPUnit irá converter para exceções. Se você está tendo problemas com essa funcionalidade, certifique-se que o PHP não está configurado para suprimir os tipos de erros que você esta testando.

Exemplo 2.13: Esperando um erro PHP usando @expectedException

<?php
class ExpectedErrorTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException PHPUnit_Framework_Error
     */
    public function testFailingInclude()
    {
        include 'not_existing_file.php';
    }
}
?>
phpunit -d error_reporting=2 ExpectedErrorTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

.

Time: 0 seconds, Memory: 5.25Mb

OK (1 test, 1 assertion)

PHPUnit_Framework_Error_Notice e PHPUnit_Framework_Error_Warning representam notificações e avisos do PHP, respectivamente.

Nota

Você deve ser o mais específico possível quando testar exceções. Testar por classes que são muito genéricas pode causar efeitos colaterais indesejáveis. Da mesma forma, testar para a classe Exception com @expectedException ou setExpectedException() não é mais permitido.

Ao testar com funções que dependem de funções php que disparam erros como fopen pode ser útil algumas vezes usar a supressão de erros enquanto testa. Isso permite a você verificar os valores retornados por suprimir notificações que levariam a uma PHPUnit_Framework_Error_Notice phpunit.

Exemplo 2.14: Testando valores de retorno de código que utiliza PHP Errors

<?php
class ErrorSuppressionTest extends PHPUnit_Framework_TestCase
{
    public function testFileWriting() {
        $writer = new FileWriter;
        $this->assertFalse(@$writer->write('/is-not-writeable/file', 'stuff'));
    }
}
class FileWriter
{
    public function write($file, $content) {
        $file = fopen($file, 'w');
        if($file == false) {
            return false;
        }
        // ...
    }
}

?>
phpunit ErrorSuppressionTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

.

Time: 1 seconds, Memory: 5.25Mb

OK (1 test, 1 assertion)


Sem a supressão de erros o teste teria relatado uma falha fopen(/is-not-writeable/file): failed to open stream: No such file or directory.

Testando Saídas

Às vezes você quer assegurar que a execução de um método, por exemplo, gere uma saída esperada (via echo ou print, por exemplo). A classe PHPUnit_Framework_TestCase usa a funcionalidade Output Buffering do PHP para fornecer a funcionalidade que é necessária para isso.

Exemplo 2.15 mostra como usar o método expectOutputString() para definir a saída esperada. Se essa saída esperada não for gerada, o teste será contado como uma falha.

Exemplo 2.15: Testando a saída de uma função ou método

<?php
class OutputTest extends PHPUnit_Framework_TestCase
{
    public function testExpectFooActualFoo()
    {
        $this->expectOutputString('foo');
        print 'foo';
    }

    public function testExpectBarActualBaz()
    {
        $this->expectOutputString('bar');
        print 'baz';
    }
}
?>
phpunit OutputTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

.F

Time: 0 seconds, Memory: 5.75Mb

There was 1 failure:

1) OutputTest::testExpectBarActualBaz
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'bar'
+'baz'


FAILURES!
Tests: 2, Assertions: 2, Failures: 1.

Tabela 2.2 mostra os métodos fornecidos para testar saídas.

Tabela 2.2. Métodos para testar a saída

MétodoSignificado
void expectOutputRegex(string $regularExpression)Define a saída que se espera combinar com a $regularExpression.
void expectOutputString(string $expectedString)Define a saída que se espera ser igual a uma $expectedString.
booleano setOutputCallback(callable $callback)Define um callback que é usado, por exemplo, para normalizar a saída real.

Nota

Um teste que emite saída irá falhar no modo estrito.

Saída de Erro

Sempre que um teste falha o PHPUnit faz o melhor para fornecer a você o máximo possível de conteúdo que possa ajudar a identificar o problema.

Exemplo 2.16: Saída de erro gerada quando uma comparação de vetores falha

<?php
class ArrayDiffTest extends PHPUnit_Framework_TestCase
{
    public function testEquality() {
        $this->assertEquals(
            array(1,2,3 ,4,5,6),
            array(1,2,33,4,5,6)
        );
    }
}
?>
phpunit ArrayDiffTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) ArrayDiffTest::testEquality
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
 Array (
     0 => 1
     1 => 2
-    2 => 3
+    2 => 33
     3 => 4
     4 => 5
     5 => 6
 )

/home/sb/ArrayDiffTest.php:7

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Neste exemplo apenas um dos valores dos vetores diferem e os outros valores são exibidos para fornecer o contexto onde o erro ocorreu.

Quando a saída gerada for longa demais para ler o PHPUnit vai quebrá-la e fornecer algumas linhas de contexto ao redor de cada diferença.

Exemplo 2.17: Saída de erro quando uma comparação de um vetor longo falha

<?php
class LongArrayDiffTest extends PHPUnit_Framework_TestCase
{
    public function testEquality() {
        $this->assertEquals(
            array(0,0,0,0,0,0,0,0,0,0,0,0,1,2,3 ,4,5,6),
            array(0,0,0,0,0,0,0,0,0,0,0,0,1,2,33,4,5,6)
        );
    }
}
?>
phpunit LongArrayDiffTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) LongArrayDiffTest::testEquality
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
     13 => 2
-    14 => 3
+    14 => 33
     15 => 4
     16 => 5
     17 => 6
 )


/home/sb/LongArrayDiffTest.php:7

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Casos Extremos

Quando uma comparação falha o PHPUnit cria uma representação textual da entrada de valores e as compara. Devido a essa implementação uma diferenciação pode mostrar mais problemas do que realmente existem.

Isso só acontece quando se usa assertEquals ou outra função de comparação 'fraca' em vetores ou objetos.

Exemplo 2.18: Caso extremo na geração de diferenciação quando se usa uma comparação fraca

<?php
class ArrayWeakComparisonTest extends PHPUnit_Framework_TestCase
{
    public function testEquality() {
        $this->assertEquals(
            array(1  ,2,3 ,4,5,6),
            array('1',2,33,4,5,6)
        );
    }
}
?>
phpunit ArrayWeakComparisonTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) ArrayWeakComparisonTest::testEquality
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
 Array (
-    0 => 1
+    0 => '1'
     1 => 2
-    2 => 3
+    2 => 33
     3 => 4
     4 => 5
     5 => 6
 )


/home/sb/ArrayWeakComparisonTest.php:7

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Neste exemplo a diferença no primeiro índice entre 1 e '1' é relatada ainda que o assertEquals considere os valores como uma combinação.

Capítulo 3. O executor de testes em linha-de-comando

O executor de testes em linha-de-comando do PHPUnit pode ser invocado através do comando phpunit. O código seguinte mostra como executar testes com o executor de testes em linha-de-comando do PHPUnit:

phpunit ArrayTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

..

Time: 0 seconds


OK (2 tests, 2 assertions)

Quando invocado como mostrado acima, o executor de testes em linha-de-comando do PHPUnit irá procurar por um arquivo-fonte ArrayTest.php no diretório de trabalho atual, carregá-lo, e espera encontrar uma classe de caso de teste ArrayTest. Irá então executar os testes desta classe.

Para cada teste executado, a ferramenta de linha-de-comando do PHPUnit imprime um caractere para indicar o progresso:

.

Impresso quando um teste é bem sucedido.

F

Impresso quando uma asserção falha enquanto o método de teste executa.

E

Impresso quando um erro ocorre enquanto o método de teste executa.

R

Impresso quando o teste foi marcado como arriscado (veja Capítulo 6).

S

Impresso quando o teste é pulado (veja Capítulo 7).

I

Impresso quando o teste é marcado como incompleto ou ainda não implementado (veja Capítulo 7).

O PHPUnit distingue entre falhas e erros. Uma falha é uma asserção do PHPUnit violada assim como uma chamada falha ao assertEquals(). Um erro é uma exceção inesperada ou um erro do PHP. Às vezes essa distinção se mostra útil já que erros tendem a ser mais fáceis de consertar do que falhas. Se você tiver uma grande lista de problemas, é melhor enfrentar os erros primeiro e ver se quaisquer falhas continuam depois de todos consertados.

Opções de linha-de-comando

Vamos dar uma olhada nas opções do executor de testes em linha-de-comando no código seguinte:

phpunit --help
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

Usage: phpunit [options] UnitTest [UnitTest.php]
       phpunit [options] <directory>

Code Coverage Options:

  --coverage-clover <file>  Generate code coverage report in Clover XML format.
  --coverage-crap4j <file>  Generate code coverage report in Crap4J XML format.
  --coverage-html <dir>     Generate code coverage report in HTML format.
  --coverage-php <file>     Export PHP_CodeCoverage object to file.
  --coverage-text=<file>    Generate code coverage report in text format.
                            Default: Standard output.
  --coverage-xml <dir>      Generate code coverage report in PHPUnit XML format.

Logging Options:

  --log-junit <file>        Log test execution in JUnit XML format to file.
  --log-tap <file>          Log test execution in TAP format to file.
  --log-json <file>         Log test execution in JSON format.
  --testdox-html <file>     Write agile documentation in HTML format to file.
  --testdox-text <file>     Write agile documentation in Text format to file.

Test Selection Options:

  --filter <pattern>        Filter which tests to run.
  --testsuite <pattern>     Filter which testsuite to run.
  --group ...               Only runs tests from the specified group(s).
  --exclude-group ...       Exclude tests from the specified group(s).
  --list-groups             List available test groups.
  --test-suffix ...         Only search for test in files with specified
                            suffix(es). Default: Test.php,.phpt

Test Execution Options:

  --report-useless-tests    Be strict about tests that do not test anything.
  --strict-coverage         Be strict about unintentionally covered code.
  --strict-global-state     Be strict about changes to global state
  --disallow-test-output    Be strict about output during tests.
  --enforce-time-limit      Enforce time limit based on test size.
  --disallow-todo-tests     Disallow @todo-annotated tests.

  --process-isolation       Run each test in a separate PHP process.
  --no-globals-backup       Do not backup and restore $GLOBALS for each test.
  --static-backup           Backup and restore static attributes for each test.

  --colors=<flag>           Use colors in output ("never", "auto" or "always").
  --columns <n>             Number of columns to use for progress output.
  --columns max             Use maximum number of columns for progress output.
  --stderr                  Write to STDERR instead of STDOUT.
  --stop-on-error           Stop execution upon first error.
  --stop-on-failure         Stop execution upon first error or failure.
  --stop-on-risky           Stop execution upon first risky test.
  --stop-on-skipped         Stop execution upon first skipped test.
  --stop-on-incomplete      Stop execution upon first incomplete test.
  -v|--verbose              Output more verbose information.
  --debug                   Display debugging information during test execution.

  --loader <loader>         TestSuiteLoader implementation to use.
  --repeat <times>          Runs the test(s) repeatedly.
  --tap                     Report test execution progress in TAP format.
  --testdox                 Report test execution progress in TestDox format.
  --printer <printer>       TestListener implementation to use.

Configuration Options:

  --bootstrap <file>        A "bootstrap" PHP file that is run before the tests.
  -c|--configuration <file> Read configuration from XML file.
  --no-configuration        Ignore default configuration file (phpunit.xml).
  --include-path <path(s)>  Prepend PHP's include_path with given path(s).
  -d key[=value]            Sets a php.ini value.

Miscellaneous Options:

  -h|--help                 Prints this usage information.
  --version                 Prints the version and exits.
phpunit UnitTest

Executa os testes que são fornecidos pela classe UnitTest. Espera-se que essa classe seja declarada no arquivo-fonte UnitTest.php.

UnitTest deve ser ou uma classe que herda de PHPUnit_Framework_TestCase ou uma classe que fornece um método public static suite() que retorna um objeto PHPUnit_Framework_Test, por exemplo uma instância da classe PHPUnit_Framework_TestSuite.

phpunit UnitTest UnitTest.php

Executa os testes que são fornecidos pela classe UnitTest. Espera-se que esta classe seja declarada no arquivo-fonte especificado.

--coverage-clover

Gera uma arquivo de registro no formato XML com as informações da cobertura de código para a execução dos testes. Veja Capítulo 14 para mais detlahes.

Por favor, note que essa funcionalidade está disponível somente quando as extensões tokenizer e Xdebug estão instaladas.

--coverage-crap4j

Gera um relatório de cobertura de código no formato Crap4j. Veja Capítulo 11 para mais detalhes.

Por favor, note que essa funcionalidade está disponível somente quando as extensões tokenizer e Xdebug estão instaladas.

--coverage-html

Gera um relatório no formato HTML. Veja Capítulo 11 para mais detalhes.

--coverage-php

Gera um objeto PHP_CodeCoverage serializado com as informações de cobertura de código.

Por favor, note que essa funcionalidade está disponível somente quando as extensões tokenizer e Xdebug estão instaladas.

--coverage-text

Gera um arquivo de registro ou saída de linha de comando no formato legível por humanos com a informação de cobertura de código para a execução dos testes. Veja Capítulo 14 para mais detalhes.

Por favor, note que essa funcionalidade está disponível somente quando as extensões tokenizer e Xdebug estão instaladas.

--log-junit

Gera um arquivo de registro no formato XML Junit para a execução dos testes. Veja Capítulo 14 para mais detalhes.

--log-tap

Gera um arquivo de registro usando o formato Test Anything Protocol (TAP) para a execução dos testes. Veja Capítulo 14 para mais detalhes.

--log-json

Gera um arquivo de registro usando o formato JSON. Veja Capítulo 14 para mais detalhes.

--testdox-html e --testdox-text

Gera documentação ágil no formato HTML ou texto plano para os testes que são executados. Veja Capítulo 12 para mais detalhes.

--filter

Apenas executa os testes cujos nomes combinam com o padrão fornecido. Se o padrão não for colocado entre delimitadores, o PHPUnit irá colocar o padrão no delimitador /.

Os nomes de teste para combinar estará em um dos seguintes formatos:

TestNamespace\TestCaseClass::testMethod

O formato do nome de teste padrão é o equivalente ao usar a constante mágica __METHOD__ dentro do método de teste.

TestNamespace\TestCaseClass::testMethod with data set #0

Quando um teste tem um provedor de dados, cada iteração dos dados obtém o índice atual acrescido ao final do nome do teste padrão.

TestNamespace\TestCaseClass::testMethod with data set "my named data"

Quando um teste tem um provedor de dados que usa conjuntos nomeados, cada iteração dos dados obtém o nome atual acrescido ao final do nome do teste padrão. Veja Exemplo 3.1 para um exemplo de conjunto de dados nomeados.

Exemplo 3.1: Conjunto de dados nomeados

<?php
namespace TestNamespace;

class TestCaseClass extends \PHPUnit_Framework_TestCase
{
    /**
     * @dataProvider provider
     */
    public function testMethod($data)
    {
        $this->assertTrue($data);
    }

    public function provider()
    {
        return array(
           'my named data' => array(true),
           'my data'       => array(true)
        );
    }
}
?>

/path/to/my/test.phpt

O nome do teste para um teste PHPT é o caminho do sistema de arquivos.

Veja Exemplo 3.2 para exemplos de padrões de filtros válidos.

Exemplo 3.2: Exmplos de padrão de filtro

  • --filter 'TestNamespace\\TestCaseClass::testMethod'

  • --filter 'TestNamespace\\TestCaseClass'

  • --filter TestNamespace

  • --filter TestCaseClass

  • --filter testMethod

  • --filter '/::testMethod .*"my named data"/'

  • --filter '/::testMethod .*#5$/'

  • --filter '/::testMethod .*#(5|6|7)$/'


Veja Exemplo 3.3 para alguns atalhos adicionais que estão disponíveis para combinar provedores de dados

Exemplo 3.3: Atalhos de filtro

  • --filter 'testMethod#2'

  • --filter 'testMethod#2-4'

  • --filter '#2'

  • --filter '#2-4'

  • --filter 'testMethod@my named data'

  • --filter 'testMethod@my.*data'

  • --filter '@my named data'

  • --filter '@my.*data'


--testsuite

Só roda o conjunto de teste cujo nome combina com o padrão dado.

--group

Apenas executa os testes do(s) grupo(s) especificado(s). Um teste pode ser marcado como pertencente a um grupo usando a anotação @group.

A anotação @author é um apelido para @group, permitindo filtrar os testes com base em seus autores.

--exclude-group

Exclui testes do(s) grupo(s) especificado(s). Um teste pode ser marcado como pertencente a um grupo usando a anotação @group.

--list-groups

Lista os grupos de teste disponíveis.

--test-suffix

Só procurar por arquivos de teste com o sufixo(s) especificado..

--report-useless-tests

Seja estrito sobre testes que não testam nada. Veja Capítulo 6 para detalhes.

--strict-coverage

Seja estrito sobre a cobertura de código involuntariamente. Veja Capítulo 6 para detalhes.

--strict-global-state

Seja estrito sobre manipulação de estado global. Veja Capítulo 6 para detalhes.

--disallow-test-output

Seja estrito sobre a saída durante os testes. Veja Capítulo 6 para detalhes.

--disallow-todo-tests

Não executa teste que tem a anotação @todo em seu bloco de documentação.

--enforce-time-limit

Impõem limite de tempo baseado no tamanho do teste. Veja Capítulo 6 para detalhes.

--process-isolation

Executa cada teste em um processo PHP separado.

--no-globals-backup

Não faz cópia de segurança e restaura $GLOBALS. Veja “Estado Global” para mais detalhes.

--static-backup

Faz cópia de segurança e restaura atributos estáticos das classes definidas pelo usuário. Veja “Estado Global” para mais detalhes.

--colors

Usa cores na saída. No Windows, usar ANSICON ou ConEmu.

--stderr

Opcionalmente imprime para STDERR em vez de STDOUT.

--stop-on-error

Para a execução no primeiro erro.

--stop-on-failure

Para a execução no primeiro erro ou falha.

--stop-on-risky

Para a execução no primeiro teste arriscado.

--stop-on-skipped

Para a execução no primeiro teste pulado.

--stop-on-incomplete

Para a execução no primeiro teste incompleto.

--verbose

Saída mais verbosa de informações, por exemplo os nomes dos testes que ficaram incompletos ou foram pulados.

--debug

Informação de saída da depuração como o nome de um teste quando a execução começa.

--loader

Especifica a implementação PHPUnit_Runner_TestSuiteLoader a ser usada.

O carregador de suíte de teste padrão irá procurar pelo arquivo-fonte no diretório de trabalho atual e em cada diretório que está especificado na configuração de diretiva include_path do PHP. Um nome de classe como Project_Package_Class é mapeado para o nome de arquivo-fonte Project/Package/Class.php.

--repeat

Executa repetidamente o(s) teste(s) um determinado número de vezes.

--tap

Relata o progresso do teste usando o Test Anything Protocol (TAP). Veja Capítulo 14 para mais detalhes.

--testdox

Relata o progresso do teste como uma documentação ágil. Veja Capítulo 12 para mais detalhes.

--printer

Especifica o impressor de resultados a ser usado. A classe impressora deve estender PHPUnit_Util_Printer e implementar a interface PHPUnit_Framework_TestListener.

--bootstrap

Um arquivo PHP "bootstrap" que é executado antes dos testes.

--configuration, -c

Lê a configuração de um arquivo XML. Veja Apêndice C para mais detalhes.

Se phpunit.xml ou phpunit.xml.dist (nessa ordem) existirem no diretório de trabalho atual e --configuration não for usado, a configuração será lida automaticamente desse arquivo.

--no-configuration

Ignora phpunit.xml e phpunit.xml.dist do diretório de trabalho atual.

--include-path

Precede o include_path do PHP com o(s) caminho(s) fornecido(s).

-d

Define o valor da opção de configuração do PHP fornecida.

Nota

Por favor, note que as opções não devem ser colocadas depois do(s) argumento(s).

Capítulo 4. Ambientes

Uma das partes que mais consomem tempo ao se escrever testes é escrever o código para ajustar o ambiente para um estado conhecido e então retorná-lo ao seu estado original quando o teste está completo. Esse estado conhecido é chamado de ambiente do teste.

Em Exemplo 2.1, o ambiente era simplesmente o vetor que está guardado na variável $stack. Na maior parte do tempo, porém, o ambiente será mais complexo que um simples vetor, e a quantidade de código necessária para defini-lo aumentará na mesma proporção. O conteúdo real do teste se perde na bagunça da configuração do ambiente. Esse problema piora ainda mais quando você escreve vários testes com ambientes similares. Sem alguma ajuda do framework de teste, teríamos que duplicar o código que define o ambiente para cada teste que escrevermos.

O PHPUnit suporta compartilhamento do código de configuração. Antes que um método seja executado, um método modelo chamado setUp() é invocado. setUp() é onde você cria os objetos que serão alvo dos testes. Uma vez que o método de teste tenha terminado sua execução, seja bem-sucedido ou falho, outro método modelo chamado tearDown() é invocado. tearDown() é onde você limpa os objetos que foram alvo dos testes.

Em Exemplo 2.2 usamos a relação produtor-consumidor entre testes para compartilhar ambientes. Isso nem sempre é desejável, ou mesmo possível. Exemplo 4.1 mostra como podemos escrever os testes do StackTest de forma que o próprio ambiente não é reutilizado, mas o código que o cria. Primeiro declaramos a variável de instância $stack, que usaremos no lugar de uma variável do método local. Então colocamos a criação do ambiente vetor dentro do método setUp(). Finalmente, removemos o código redundante dos métodos de teste e usamos a nova variável de instância, $this->stack, em vez da variável de método local $stack com o método de asserção assertEquals().

Exemplo 4.1: Usando setUp() para criar o ambiente stack

<?php
class StackTest extends PHPUnit_Framework_TestCase
{
    protected $stack;

    protected function setUp()
    {
        $this->stack = array();
    }

    public function testEmpty()
    {
        $this->assertTrue(empty($this->stack));
    }

    public function testPush()
    {
        array_push($this->stack, 'foo');
        $this->assertEquals('foo', $this->stack[count($this->stack)-1]);
        $this->assertFalse(empty($this->stack));
    }

    public function testPop()
    {
        array_push($this->stack, 'foo');
        $this->assertEquals('foo', array_pop($this->stack));
        $this->assertTrue(empty($this->stack));
    }
}
?>

Os métodos-modelo setUp() e tearDown() são executados uma vez para cada método de teste (e em novas instâncias) da classe do caso de teste.

Além disso, os métodos-modelo setUpBeforeClass() e tearDownAfterClass() são chamados antes do primeiro teste da classe do caso de teste ser executado e após o último teste da classe do caso de teste ser executado, respectivamente.

O exemplo abaixo mostra todos os métodos-modelo que estão disponíveis em uma classe de casos de teste.

Exemplo 4.2: Exemplo mostrando todos os métodos-modelo disponíveis

<?php
class TemplateMethodsTest extends PHPUnit_Framework_TestCase
{
    public static function setUpBeforeClass()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function setUp()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function assertPreConditions()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public function testOne()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        $this->assertTrue(TRUE);
    }

    public function testTwo()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        $this->assertTrue(FALSE);
    }

    protected function assertPostConditions()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function tearDown()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    public static function tearDownAfterClass()
    {
        fwrite(STDOUT, __METHOD__ . "\n");
    }

    protected function onNotSuccessfulTest(Exception $e)
    {
        fwrite(STDOUT, __METHOD__ . "\n");
        throw $e;
    }
}
?>
phpunit TemplateMethodsTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

TemplateMethodsTest::setUpBeforeClass
TemplateMethodsTest::setUp
TemplateMethodsTest::assertPreConditions
TemplateMethodsTest::testOne
TemplateMethodsTest::assertPostConditions
TemplateMethodsTest::tearDown
.TemplateMethodsTest::setUp
TemplateMethodsTest::assertPreConditions
TemplateMethodsTest::testTwo
TemplateMethodsTest::tearDown
TemplateMethodsTest::onNotSuccessfulTest
FTemplateMethodsTest::tearDownAfterClass


Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) TemplateMethodsTest::testTwo
Failed asserting that <boolean:false> is true.
/home/sb/TemplateMethodsTest.php:30

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.

Mais setUp() que tearDown()

setUp() e tearDown() são bastante simétricos em teoria, mas não na prática. Na prática, você só precisa implementar tearDown() se você tiver alocado recursos externos como arquivos ou sockets no setUp(). Se seu setUp() apenas cria objetos planos do PHP, você pode geralmente ignorar o tearDown(). Porém, se você criar muitos objetos em seu setUp(), você pode querer unset() as variáveis que apontam para aqueles objetos em seu tearDown() para que eles possam ser coletados como lixo. A coleta de lixo dos objetos dos casos de teste não é previsível.

Variações

O que acontece quando você tem dois testes com definições (setups) ligeiramente diferentes? Existem duas possibilidades:

  • Se o código setUp() diferir só um pouco, mova o código que difere do código do setUp() para o método de teste.

  • Se você tiver um setUp() realmente diferente, você precisará de uma classe de caso de teste diferente. Nomeie a classe após a diferença na configuração.

Compartilhando Ambientes

Existem algumas boas razões para compartilhar ambientes entre testes, mas na maioria dos casos a necessidade de compartilhar um ambiente entre testes deriva de um problema de design não resolvido.

Um bom exemplo de um ambiente que faz sentido compartilhar através de vários testes é a conexão ao banco de dados: você loga no banco de dados uma vez e reutiliza essa conexão em vez de criar uma nova conexão para cada teste. Isso faz seus testes serem executados mais rápido.

Exemplo 4.3 usa os métodos-modelo setUpBeforeClass() e tearDownAfterClass() para conectar ao banco de dados antes do primeiro teste da classe de casos de teste e para desconectar do banco de dados após o último teste dos casos de teste, respectivamente.

Exemplo 4.3: Compartilhando ambientes entre os testes de uma suíte de testes

<?php
class DatabaseTest extends PHPUnit_Framework_TestCase
{
    protected static $dbh;

    public static function setUpBeforeClass()
    {
        self::$dbh = new PDO('sqlite::memory:');
    }

    public static function tearDownAfterClass()
    {
        self::$dbh = NULL;
    }
}
?>

Não dá pra enfatizar o suficiente o quanto o compartilhamento de ambientes entre testes reduz o custo dos testes. O problema de design subjacente é que objetos não são de baixo acoplamento. Você vai conseguir melhores resultados resolvendo o problema de design subjacente e então escrevendo testes usando pontas (veja Capítulo 9), do que criando dependências entre os testes em tempo de execução e ignorando a oportunidade de melhorar seu design.

Estado Global

É difícil testar um código que usa singletons (instâncias únicas de objetos). Isso também vale para os códigos que usam variáveis globais. Tipicamente, o código que você quer testar é fortemente acoplado com uma variável global e você não pode controlar sua criação. Um problema adicional é o fato de que uma alteração em uma variável global para um teste pode quebrar um outro teste.

Em PHP, variáveis globais trabalham desta forma:

  • Uma variável global $foo = 'bar'; é guardada como $GLOBALS['foo'] = 'bar';.

  • A variável $GLOBALS é chamada de variável super-global.

  • Variáveis super-globais são variáveis embutidas que estão sempre disponíveis em todos os escopos.

  • No escopo de uma função ou método, você pode acessar a variável global $foo tanto por acesso direto à $GLOBALS['foo'] ou usando global $foo; para criar uma variável local com uma referência à variável global.

Além das variáveis globais, atributos estáticos de classes também são parte do estado global.

Por padrão, o PHPUnit executa seus testes de forma que mudanças às variáveis globais ou super-globais ($GLOBALS, $_ENV, $_POST, $_GET, $_COOKIE, $_SERVER, $_FILES, $_REQUEST) não afetem outros testes. Opcionalmente, este isolamento pode ser estendido a atributos estáticos de classes.

Nota

A operações de cópia de segurança e restauração para variáveis globais e atributos estáticos de classes usa serialize() e unserialize().

Objetos de algumas classes (e.g., PDO) não podem ser serializadas e a operação de cópia de segurança vai quebrar quando esse tipo de objeto for guardado no vetor $GLOBALS, por exemplo.

A anotação @backupGlobals que é discutida na “@backupGlobals” pode ser usada para controlar as operações de cópia de segurança e restauração para variáveis globais. Alternativamente, você pode fornecer uma lista-negra de variáveis globais que deverão ser excluídas das operações de backup e restauração como esta:

class MyTest extends PHPUnit_Framework_TestCase
{
    protected $backupGlobalsBlacklist = array('globalVariable');

    // ...
}

Nota

Definir a propriedade $backupGlobalsBlacklist dentro do método setUp(), por exemplo, não tem efeito.

A anotação @backupStaticAttributes que é discutida na “@backupStaticAttributes” pode ser usado para fazer backup de todos os valores de propriedades estáticas em todas as classes declaradas antes de cada teste e restaurá-los depois.

Ele processa todas as classes que são declaradas no momento que um teste começa, não só a classe de teste. Ele só se aplica a propriedades da classe estática, e não variáveis estáticas dentro de funções.

Nota

A operação @backupStaticAttributes é executada antes de um método de teste, mas somente se ele está habilitado. Se um valor estático foi alterado por um teste executado anteriormente que não tinha ativado @backupStaticAttributes, esse valor então será feito o backup e restaurado - não o valor padrão originalmente declarado. O PHP não registra o valor padrão originalmente declarado de nenhuma variável estática.

O mesmo se aplica a propriedades estáticas de classes que foram recém-carregadas/declaradas dentro de um teste. Elas não podem ser redefinidas para o seu valor padrão originalmente declarado após o teste, uma vez que esse valor é desconhecido. Qualquer que seja o valor definido irá vazar para testes subsequentes.

Para testes de unidade, recomenda-se redefinir explicitamente os valores das propriedades estáticas em teste em seu código setUp() ao invés (e, idealmente, também tearDown(), de modo a não afetar os testes posteriormente executados).

Você pode fornecer uma lista-negra de atributos estáticos que serão excluídos das operações de cópia de segurança e restauração:

class MyTest extends PHPUnit_Framework_TestCase
{
    protected $backupStaticAttributesBlacklist = array(
      'className' => array('attributeName')
    );

    // ...
}

Nota

Definir a propriedade $backupStaticAttributesBlacklist dentro do método setUp() , por exemplo, não tem efeito.

Capítulo 5. Organizando Testes

Um dos objetivos do PHPUnit é que os testes devem ser combináveis: queremos ser capazes de executar qualquer quantidade ou combinação de testes juntos, por exemplo todos os testes para um projeto inteiro, ou os testes para todas as classes de um ambiente que é parte do projeto, ou apenas os testes para uma só classe.

O PHPUnit suporta diferentes formas de organizar testes e combiná-los em uma suíte de testes. Este capítulo mostra as abordagens mais comuns.

Compondo uma Suíte de Testes usando o Sistema de Arquivos

Provavelmente o jeito mais fácil de compor uma suíte de testes é manter todos os arquivos-fonte dos casos de teste em um diretório de testes. O PHPUnit pode descobrir automaticamente e executar os testes atravessando recursivamente o diretório de testes.

Vamos dar uma olhada na suíte de testes da biblioteca sebastianbergmann/money. Observando a estrutura de diretórios desse projeto, podemos ver que as classes dos casos de teste no diretório tests espelha o pacote e estrutura de classes do Sistema Sob Teste (SST – ou SUT: System Under Teste) no diretório src:

src                                 tests
`-- Currency.php                    `-- CurrencyTest.php
`-- IntlFormatter.php               `-- IntlFormatterTest.php
`-- Money.php                       `-- MoneyTest.php
`-- autoload.php

Para executar todos os testes para a biblioteca precisamos apenas apontar o executor de testes em linha-de-comando do PHPUnit para o diretório de teste:

phpunit --bootstrap src/autoload.php tests
PHPUnit 5.7.0 by Sebastian Bergmann.

.................................

Time: 636 ms, Memory: 3.50Mb

OK (33 tests, 52 assertions)

Nota

Se você apontar o executor de testes em linha-de-comando do PHPUnit para um diretório, ele irá procurar por arquivos *Test.php.

Para executar apenas os testes declarados na classe de casos de teste CurrencyTest em tests/CurrencyTest.php, podemos usar o seguinte comando:

phpunit --bootstrap src/autoload.php tests/CurrencyTest
PHPUnit 5.7.0 by Sebastian Bergmann.

........

Time: 280 ms, Memory: 2.75Mb

OK (8 tests, 8 assertions)

Para um controle mais refinado sobre quais testes executar, podemos usar a opção --filter:

phpunit --bootstrap src/autoload.php --filter testObjectCanBeConstructedForValidConstructorArgument tests
PHPUnit 5.7.0 by Sebastian Bergmann.

..

Time: 167 ms, Memory: 3.00Mb

OK (2 test, 2 assertions)

Nota

Uma desvantagem dessa abordagem é que não temos controle sobre a ordem em que os testes são executados. Isso pode causar problemas com relação às dependências dos testes, veja “Dependências de Testes”. Na próxima seção você vai ver como pode tornar explícita a ordem de execução de testes usando o arquivo de configuração XML.

Compondo uma Suíte de Testes Usando uma Configuração XML

O arquivo de configuração XML do PHPUnit (Apêndice C) também pode ser usado para compor uma suíte de testes. Exemplo 5.1 mostra um arquivo mínimo phpunit.xml que adicionará todas as classes *Test que forem encontradas em arquivos *Test.php quando o diretório tests é atravessados recursivamente.

Exemplo 5.1: Compondo uma Suíte de Testes Usando uma Configuração XML

<phpunit bootstrap="src/autoload.php">
  <testsuites>
    <testsuite name="money">
      <directory>tests</directory>
    </testsuite>
  </testsuites>
</phpunit>

A ordem de execução dos testes pode ser explicitada:

Se phpunit.xml ou phpunit.xml.dist (nesta ordem) existem no diretório de trabalho atual e --configuration não é usado, a configuração será automaticamente lida desse arquivo.

A ordem em que os testes são executados pode ser explicitada:

Exemplo 5.2: Compondo uma Suíte de Testes Usando uma Configuração XML

<phpunit bootstrap="src/autoload.php">
  <testsuites>
    <testsuite name="money">
      <file>tests/IntlFormatterTest.php</file>
      <file>tests/MoneyTest.php</file>
      <file>tests/CurrencyTest.php</file>
    </testsuite>
  </testsuites>
</phpunit>

Capítulo 6. Testes arriscados

PHPUnit pode realizar verificações adicionais documentadas abaixo enquanto executa os testes.

Testes Inúteis

PHPUnit pode ser estrito sobre testes que não testam nada. Esta verificação pode ser habilitada usando a opção --report-useless-tests na linha de comando ou pela definição beStrictAboutTestsThatDoNotTestAnything="true" no arquivo de configuração XML do PHPUnit.

Um teste que não realiza uma afirmação irá ser marcado como arriscado quando essa verificação está habilitada. Expectativas sobre objetos falsificados ou anotações tais como @expectedException contam com uma afirmação.

Cobertura de Código Involuntária

PHPUnit pode ser estrito sobre cobertura de código involuntária. Esta verificação pode ser habilitada usando a opção --strict-coverage na linha de comando ou pela definição checkForUnintentionallyCoveredCode="true" no arquivo de configuração XML do PHPUnit.

Um teste que é anotado com @covers e executa código que não está na lista utilizando uma anotação @covers ou @uses será marcado como arriscado quando essa verificação é habilitada.

Saída Durante a Execução de Teste

PHPUnit pode ser estrito sobre a saída durante os testes. Esta verificação pode ser habilitada usando a opção --disallow-test-output na linha de comando ou pela definição beStrictAboutOutputDuringTests="true" no arquivo de configuração XML do PHPUnit.

Um teste que emite saída, por exemplo pela invocação de print tanto no código de teste ou no código testado, será marcado como arriscado quando esta verificação está habilitada.

Tempo de Espera de Execução de Teste

Um limite de tempo pode ser forçado para a execução de um teste se o pacote PHP_Invoker está instalado e a extensão pcntl está disponível. A imposição deste tempo de espera pode ser habilitado pelo uso da opção --enforce-time-limit na linha de comando ou pela definição beStrictAboutTestSize="true" no arquivo de configuração XML do PHPUnit.

Um teste anotado com @large irá falhar se ele demorar mais que 60 segundos para executar. Esse tempo de espera é configurável através do atributo timeoutForLargeTests no arquivo de configuração XML.

Um teste anotado com @medium irá falhar se ele demorar mais que 10 segundos para executar. Esse tempo de espera é configurável através do atributo timeoutForMediumTests no arquivo de configuração XML.

Um teste que não é anotado com @medium ou @large será tratado como se fosse anotado com @small. Um teste pequeno irá falhar se demorar mais que 1 segundo para executar. Esse tempo de espera é configurável através do atributo timeoutForSmallTests no arquivo de configuração XML.

Manipulação do Estado Global

PHPUnit pode ser estrito sobre testes que manipulam o estado global. Esta verificação pode ser habilitada usando a opção --strict-global-state na linha de comando ou pela definição beStrictAboutOutputDuringTests="true" no arquivo de configuração XML do PHPUnit.

Capítulo 7. Testes Incompletos e Pulados

Testes Incompletos

Quando você está trabalhando em uma nova classe de caso de teste, você pode querer começar a escrever métodos de teste vazios, como:

public function testSomething()
{
}

para manter o controle sobre os testes que você já escreveu. O problema com os métodos de teste vazios é que eles são interpretados como bem-sucedidos pelo framework do PHPUnit. Esse erro de interpretação leva à inutilização dos relatórios de testes -- você não pode ver se um teste foi realmente bem-sucedido ou simplesmente ainda não foi implementado. Chamar $this->fail() no teste não implementado não ajuda em nada, já que o teste será interpretado como uma falha. Isso seria tão errado quanto interpretar um teste não implementado como bem-sucedido.

Se imaginarmos que um teste bem-sucedido é uma luz verde e um teste mal-sucedido (falho) é uma luz vermelha, precisaremos de uma luz amarela adicional para marcar um teste como incompleto ou ainda não implementado. O PHPUnit_Framework_IncompleteTest é uma interface marcadora para marcar uma exceção que surge de um método de teste como resultado do teste ser incompleto ou atualmente não implementado. O PHPUnit_Framework_IncompleteTestError é a implementação padrão dessa interface.

O Exemplo 7.1 mostra uma classe de caso de teste, SampleTest, que contém um método de teste, testSomething(). Chamando o método de conveniência markTestIncomplete() (que automaticamente traz uma exceção PHPUnit_Framework_IncompleteTestError) no método de teste, marcamos o teste como sendo incompleto.

Exemplo 7.1: Marcando um teste como incompleto

<?php
class SampleTest extends PHPUnit_Framework_TestCase
{
    public function testSomething()
    {
        // Opcional: Teste alguma coisa aqui, se quiser
        $this->assertTrue(TRUE, 'This should already work.');

        // Pare aqui e marque este teste como incompleto.
        $this->markTestIncomplete(
          'This test has not been implemented yet.'
        );
    }
}
?>

Um teste incompleto é denotado por um I na saída do executor de testes em linha-de-comando do PHPUnit, como mostrado no exemplo abaixo:

phpunit --verbose SampleTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

I

Time: 0 seconds, Memory: 3.95Mb

There was 1 incomplete test:

1) SampleTest::testSomething
This test has not been implemented yet.

/home/sb/SampleTest.php:12
OK, but incomplete or skipped tests!
Tests: 1, Assertions: 1, Incomplete: 1.

A Tabela 7.1 mostra a API para marcar testes como incompletos.

Tabela 7.1. API para Testes Incompletos

MétodoSignificado
void markTestIncomplete()Marca o teste atual como incompleto.
void markTestIncomplete(string $message)Marca o teste atual como incompleto usando $message como uma mensagem explanatória.

Pulando Testes

Nem todos os testes podem ser executados em qualquer ambiente. Considere, por exemplo, uma camada de abstração de banco de dados contendo vários drivers para os diversos sistemas de banco de dados que suporta. Os testes para o driver MySQL podem ser executados apenas, é claro, se um servidor MySQL estiver disponível.

O Exemplo 7.2 mostra uma classe de caso de teste, DatabaseTest, que contém um método de teste, testConnection(). No método-modelo setUp() da classe de caso de teste, verificamos se a extensão MySQLi está disponível e usamos o método markTestSkipped() para pular o teste caso contrário.

Exemplo 7.2: Pulando um teste

<?php
class DatabaseTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        if (!extension_loaded('mysqli')) {
            $this->markTestSkipped(
              'The MySQLi extension is not available.'
            );
        }
    }

    public function testConnection()
    {
        // ...
    }
}
?>

Um teste que tenha sido pulado é denotado por um S na saída do executor de testes em linha-de-comando do PHPUnit, como mostrado no seguinte exemplo:

phpunit --verbose DatabaseTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

S

Time: 0 seconds, Memory: 3.95Mb

There was 1 skipped test:

1) DatabaseTest::testConnection
The MySQLi extension is not available.

/home/sb/DatabaseTest.php:9
OK, but incomplete or skipped tests!
Tests: 1, Assertions: 0, Skipped: 1.

Tabela 7.2 mostra a API para pular testes.

Tabela 7.2. API para Pular Testes

MétodoSignificado
void markTestSkipped()Marca o teste atual como pulado.
void markTestSkipped(string $message)Marca o teste atual como pulado usando $message como uma mensagem explanatória.

Pulando Testes usando @requires

Além do método acima também é possível usar a anotação @requires para expressar pré-condições comuns para um caso de teste.

Tabela 7.3. Possíveis usos para @requires

TipoValores PossíveisExemplosOutro exemplo
PHPQualquer identificador de versão do PHP@requires PHP 5.3.3@requires PHP 7.1-dev
PHPUnitQualquer identificador de versão do PHPUnit@requires PHPUnit 3.6.3@requires PHPUnit 5.0
OSUma expressão regular que combine PHP_OS@requires OS Linux@requires OS WIN32|WINNT
funçãoQualquer parâmetro válido para function_exists@requires function imap_open@requires function ReflectionMethod::setAccessible
extensãoQualquer nome de extensão@requires extension mysqli@requires extension curl

Exemplo 7.3: Pulando casos de teste usando @requires

<?php
/**
 * @requires extension mysqli
 */
class DatabaseTest extends PHPUnit_Framework_TestCase
{
    /**
     * @requires PHP 5.3
     */
    public function testConnection()
    {
        // O Teste requer as extensões mysqli e PHP >= 5.3
    }

    // ... Todos os outros testes requerem a extensão mysqli
}
?>

Se você está usando uma sintaxe que não compila com uma certa versão do PHP, procure dentro da configuração xml por includes dependentes de versão na “Suítes de Teste”.

Capítulo 8. Testando Bancos de Dados

Muitos exemplos de testes unitários iniciantes e intermediários em qualquer linguagem de programação sugerem que é perfeitamente fácil testar a lógica de sua aplicação com testes simples. Para aplicações centradas em bancos de dados isso está longe da realidade. Comece a usar Wordpress, TYPO3 ou Symfony com Doctrine ou Propel, por exemplo, e você vai experimentar facilmente problemas consideráveis com o PHPUnit: apenas porque o banco de dados é fortemente acoplado com essas bibliotecas.

Você provavelmente conhece esse cenário dos seus trabalhos e projetos diários, onde você quer colocar em prática suas habilidades (novas ou não) com PHPUnit e acaba ficando preso por um dos seguintes problemas:

  1. O método que você quer testar executa uma operação JOIN muito grande e usa os dados para calcular alguns resultados importantes.

  2. Sua lógica de negócios faz uma mistura de declarações SELECT, INSERT, UPDATE e DELETE.

  3. Você precisa definir os dados de teste em (provavelmente muito) mais de duas tabelas para conseguir dados iniciais razoáveis para os métodos que deseja testar.

A extensão DbUnit simplifica consideravelmente a configuração de um banco de dados para fins de teste e permite a você verificar os conteúdos de um banco de dados após fazer uma série de operações.

Fornecedores Suportados para Testes de Banco de Dados

DbUnit atualmente suporta MySQL, PostgreSQL, Oracle e SQLite. Através das integrações Zend Framework ou Doctrine 2 ele tem acesso a outros sistemas como IBM DB2 ou Microsoft SQL Server.

Dificuldades em Testes de Bancos de Dados

Existe uma boa razão pela qual todos os exemplos de testes unitários não incluírem interações com bancos de dados: esses tipos de testes são complexos tanto em configuração quanto em manutenção. Enquanto testar contra seu banco de dados você precisará ter cuidado com as seguintes variáveis:

  • Esquema e tabelas do banco de dados

  • Inserção das linhas exigidas para o teste nessas tabelas

  • Verificação do estado do banco de dados depois de executar os testes

  • Limpeza do banco de dados para cada novo teste

Por causa de muitas APIs de bancos de dados como PDO, MySQLi ou OCI8 serem incômodos de usar e verbosas para escrever, fazer esses passos manualmente é um completo pesadelo.

O código de teste deve ser o mais curto e preciso possível por várias razões:

  • Você não quer modificar uma considerável quantidade de código de teste por pequenas mudanças em seu código de produção.

  • Você quer ser capaz de ler e entender o código de teste facilmente, mesmo meses depois de tê-lo escrito.

Adicionalmente você tem que perceber que o banco de dados é essencialmente uma variável global de entrada para seu código. Dois testes em sua suíte de testes podem executar contra o mesmo banco de dados, possivelmente reutilizando dados múltiplas vezes. Falhas em um teste podem facilmente afetar o resultado dos testes seguintes, fazendo sua experiência com os testes muito difícil. O passo de limpeza mencionado anteriormente é de maior importância para resolver o problema do banco de dados ser uma entrada global.

DbUnit ajuda a simplificar todos esses problemas com testes de bancos de dados de uma forma elegante.

O PHPUnit só não pode ajudá-lo no fato de que testes de banco de dados são muito lentos comparados aos testes que não usam bancos de dados. Dependendo do tamanho das interações com seu banco de dados, seus testes podem levar um tempo considerável para executar. Porém se você mantiver pequena a quantidade de dados usados para cada teste e tentar testar o máximo possível sem usar testes com bancos de dados, você facilmente conseguirá tempos abaixo de um minuto, mesmo para grandes suítes de teste.

A suíte de testes do projeto Doctrine 2 por exemplo, atualmente tem uma suíte com cerca de 1000 testes onde aproximadamente a metade deles tem acesso ao banco de dados e ainda executa em 15 segundos contra um banco de dados MySQL em um computador desktop comum.

Os quatro estágios dos testes com banco de dados

Em seu livro sobre Padrões de Teste xUnit, Gerard Meszaros lista os quatro estágios de um teste unitário:

  1. Configurar o ambiente (fixture)

  2. Exercitar o Sistema Sob Teste

  3. Verificar o resultado

  4. Desmontagem (Teardown)

O que é um ambiente (fixture)?

Um ambiente (fixture) descreve o estado inicial em que sua aplicação e seu banco de dados estão ao executar um teste.

Testar o banco de dados exige que você utilize pelo menos o setup (configuração) e teardown (desmontagem) para limpar e escrever em suas tabelas os dados de ambiente exigidos. Porém a extensão do banco de dados tem uma boa razão para reverter esses quatro estágios em um teste de banco de dados para assemelhar o seguinte fluxo de trabalho que é executado para cada teste:

1. Limpar o Banco de Dados

Já que sempre existe um primeiro teste que é executado contra o banco de dados, você não sabe exatamente se já existem dados nas tabelas. O PHPUnit vai executar um TRUNCATE contra todas as tabelas que você especificou para redefinir seus estados para vazio.

2. Configurar o ambiente

O PHPUnit então vai iterar sobre todas as linhas do ambiente especificado e inseri-las em suas respectivas tabelas.

3–5. Executar Teste, Verificar resultado e Desmontar (Teardown)

Depois de redefinir o banco de dados e carregá-lo com seu estado inicial, o verdadeiro teste é executado pelo PHPUnit. Esta parte do código de teste não exige conhecimento sobre a Extensão do Banco de Dados, então você pode prosseguir e testar o que quiser com seu código.

Em seu teste use uma asserção especial chamada assertDataSetsEqual() para fins de verificação, porém isso é totalmente opcional. Esta função será explicada na seção Asserções em Bancos de Dados.

Configuração de um Caso de Teste de Banco de Dados do PHPUnit

Ao usar o PHPUnit seus casos de teste vão estender a classe PHPUnit_Framework_TestCase da seguinte forma:

<?php
class MyTest extends PHPUnit_Framework_TestCase
{
    public function testCalculate()
    {
        $this->assertEquals(2, 1 + 1);
    }
}
?>

Se você quer um código de teste que trabalha com a Extensão para Banco de Dados a configuração é um pouco mais complexa e você terá que estender um TestCase abstrato diferente, exigindo que você implemente dois métodos abstratos getConnection() e getDataSet():

<?php
class MyGuestbookTest extends PHPUnit_Extensions_Database_TestCase
{
    /**
     * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
     */
    public function getConnection()
    {
        $pdo = new PDO('sqlite::memory:');
        return $this->createDefaultDBConnection($pdo, ':memory:');
    }

    /**
     * @return PHPUnit_Extensions_Database_DataSet_IDataSet
     */
    public function getDataSet()
    {
        return $this->createFlatXMLDataSet(dirname(__FILE__).'/_files/guestbook-seed.xml');
    }
}
?>

Implementando getConnection()

Para permitir que a limpeza e o carregamento de funcionalidades do ambiente funcionem, a Extensão do Banco de Dados do PHPUnit exige acesso a uma conexão abstrata do banco de dados através dos fornecedores da biblioteca PDO. É importante notar que sua aplicação não precisa ser baseada em PDO para usar a Extensão para Banco de Dados do PHPUnit, pois a conexão é meramente usada para limpeza e configuração de ambiente.

No exemplo anterior criamos uma conexão Sqlite na memória e a passamos ao método createDefaultDBConnection que embrulha a instância do PDO e o segundo parâmetro (o nome do banco de dados) em uma camada simples de abstração para conexões do banco de dados do tipo PHPUnit_Extensions_Database_DB_IDatabaseConnection.

A seção Usando a Conexão de Banco de Dados explica a API desta interface e como você pode usá-la da melhor forma possível.

Implementando getDataSet()

O método getDataSet() define como deve ser o estado inicial do banco de dados antes de cada teste ser executado. O estado do banco de dados é abstraído através de conceitos DataSet (Conjunto de Dados) e DataTable (Tabela de Dados), ambos sendo representados pelas interfaces PHPUnit_Extensions_Database_DataSet_IDataSet e PHPUnit_Extensions_Database_DataSet_IDataTable. A próxima seção vai descrever em detalhes como esses conceitos trabalham e quais os benefícios de usá-los nos testes com bancos de dados.

Para a implementação precisaremos apenas saber que o método getDataSet() é chamado uma vez durante o setUp() para recuperar o conjunto de dados do ambiente e inseri-lo no banco de dados. No exemplo estamos usando um método de fábrica createFlatXMLDataSet($filename) que representa um conjunto de dados através de uma representação XML.

E quanto ao Esquema do Banco de Dados (DDL)?

O PHPUnit assume que o esquema do banco de dados com todas as suas tabelas, gatilhos, sequências e visualizações é criado antes que um teste seja executado. Isso quer dizer que você como desenvolvedor deve se certificar que o banco de dados está corretamente configurado antes de executar a suíte.

Existem vários meios para realizar esta pré-condição para testar bancos de dados.

  1. Se você está usando um banco de dados persistente (não Sqlite Memory) você pode facilmente configurar o banco de dados uma vez com ferramentas como phpMyAdmin para MySQL e reutilizar o banco de dados para cada execução de teste.

  2. Se você estiver usando bibliotecas como Doctrine 2 ou Propel você pode usar suas APIs para criar o esquema de banco de dados que precisa antes de rodar os testes. Você pode utilizar as capacidades de Configuração e Bootstrap do PHPUnit para executar esse código sempre que seus testes forem executados.

Dica: Use seu próprio Caso Abstrato de Teste de Banco de Dados

Do exemplo prévio de implementação você pode facilmente perceber que o método getConnection() é bastante estático e pode ser reutilizado em diferentes casos de teste de banco de dados. Adicionalmente para manter uma boa performance dos seus testes e pouca carga sobre seu banco de dados, você pode refatorar o código um pouco para obter um caso de teste abstrato genérico para sua aplicação, o que ainda permite a você especificar um ambiente de dados diferente para cada caso de teste:

<?php
abstract class MyApp_Tests_DatabaseTestCase extends PHPUnit_Extensions_Database_TestCase
{
    // only instantiate pdo once for test clean-up/fixture load
    static private $pdo = null;

    // only instantiate PHPUnit_Extensions_Database_DB_IDatabaseConnection once per test
    private $conn = null;

    final public function getConnection()
    {
        if ($this->conn === null) {
            if (self::$pdo == null) {
                self::$pdo = new PDO('sqlite::memory:');
            }
            $this->conn = $this->createDefaultDBConnection(self::$pdo, ':memory:');
        }

        return $this->conn;
    }
}
?>

Entretanto, isso tem a conexão ao banco de dados codificada na conexão do PDO. O PHPUnit tem outra incrível característica que pode fazer este caso de teste ainda mais genérico. Se você usar a Configuração XML você pode tornar a conexão com o banco de dados configurável por execução de teste. Primeiro vamos criar um arquivo phpunit.xml em nosso diretório/de/teste da aplicação, de forma semelhante a isto:

<?xml version="1.0" encoding="UTF-8" ?>
<phpunit>
    <php>
        <var name="DB_DSN" value="mysql:dbname=myguestbook;host=localhost" />
        <var name="DB_USER" value="user" />
        <var name="DB_PASSWD" value="passwd" />
        <var name="DB_DBNAME" value="myguestbook" />
    </php>
</phpunit>

Agora podemos modificar seu caso de teste para parecer com isso:

<?php
abstract class Generic_Tests_DatabaseTestCase extends PHPUnit_Extensions_Database_TestCase
{
    // only instantiate pdo once for test clean-up/fixture load
    static private $pdo = null;

    // only instantiate PHPUnit_Extensions_Database_DB_IDatabaseConnection once per test
    private $conn = null;

    final public function getConnection()
    {
        if ($this->conn === null) {
            if (self::$pdo == null) {
                self::$pdo = new PDO( $GLOBALS['DB_DSN'], $GLOBALS['DB_USER'], $GLOBALS['DB_PASSWD'] );
            }
            $this->conn = $this->createDefaultDBConnection(self::$pdo, $GLOBALS['DB_DBNAME']);
        }

        return $this->conn;
    }
}
?>

Agora podemos executar a suíte de testes de banco de dados usando diferentes configurações através da interface de linha-de-comando:

user@desktop> phpunit --configuration developer-a.xml MyTests/
user@desktop> phpunit --configuration developer-b.xml MyTests/

A possibilidade de executar facilmente os testes de banco de dados contra diferentes alvos é muito importante se você está desenvolvendo na máquina de desenvolvimento. Se vários desenvolvedores executarem os testes de banco de dados contra a mesma conexão de banco de dados você experimentará facilmente falhas de testes devido à condição de execução.

Entendendo Conjunto de Dados e Tabelas de Dados

Um conceito central da Extensão para Banco de Dados do PHPUnit são os Conjuntos de Dados e as Tabelas de Dados. Você deveria tentar entender este conceito simples para dominar os testes de banco de dados com PHPUnit. Conjunto de Dados e Tabela de Dados formam uma camada abstrata em torno das tabelas, linhas e colunas do seu banco de dados. Uma simples API esconde os conteúdos subjacentes do banco de dados em uma estrutura de objetos, que também podem ser implementada por outra fonte que não seja um banco de dados.

Essa abstração é necessária para comparar os conteúdos reais de um banco de dados contra os conteúdos esperados. Expectativas podem ser representadas como arquivos XML, YAML, CSV ou vetores PHP, por exemplo. As interfaces DataSet (Conjunto de Dados) e DataTable (Tabela de Dados) permitem a comparação dessas fontes conceitualmente diferentes, emulando o armazenamento de banco de dados relacional em uma abordagem semanticamente similar.

Um fluxo de trabalho para asserções em banco de dados em seus testes então consiste de três passos simples:

  • Especificar uma ou mais tabelas em seu banco de dados por nome de tabela (conjunto de dados real)

  • Especificar o Conjunto de Dados esperado no seu formato preferido (YAML, XML, ...)

  • Asseverar que ambas as representações de conjunto de dados se equivalem.

Asserções não são o único caso de uso para o Conjunto de Dados e a Tabela de Dados na Extensão para Banco de Dados do PHPUnit. Como mostrado na seção anterior, eles também descrevem os conteúdos iniciais de um banco de dados. Você é forçado a definir um conjunto de dados de ambiente pelo Caso de Teste de Banco de Dados, que então é usado para:

  • Deletar todas as linhas das tabelas especificadas no conjunto de dados.

  • Escrever todas as linhas nas tabelas de dados do banco de dados.

Implementações disponíveis

Existem três tipos diferentes de conjuntos de dados/tabelas de dados:

  • Conjuntos de Dados e Tabelas de Dados baseados em arquivo

  • Conjuntos de Dados e Tabelas de Dados baseados em query

  • Filtro e Composição de Conjunto de Dados e Tabelas de Dados

Os Conjuntos de Dados e Tabelas de Dados baseadas em arquivo são geralmente usados para o ambiente inicial e para descrever os estados esperados do banco de dados.

Conjunto de Dados de XML Plano

O Conjunto de Dados mais comum é chamado XML Plano. É um formato xml simples onde uma tag dentro do nó-raiz <dataset> representa exatamente uma linha no banco de dados. Os nomes das tags equivalem à tabela onde inserir a linha e um atributo representa a coluna. Um exemplo para uma simples aplicação de livro de visitas poderia se parecer com isto:

<?xml version="1.0" ?>
<dataset>
    <guestbook id="1" content="Hello buddy!" user="joe" created="2010-04-24 17:15:23" />
    <guestbook id="2" content="I like it!" user="nancy" created="2010-04-26 12:14:20" />
</dataset>

Isso é, obviamente, fácil de escrever. Aqui <guestbook> é o nome da tabela onde duas linhas são inseridas dentro de cada com quatro colunas id, content, user e created com seus respectivos valores.

Porém essa simplicidade tem um preço.

O exemplo anterior não deixa tão óbvio como você pode fazer para especificar uma tabela vazia. Você pode inserir uma tag sem atributos com o nome da tabela vazia. Um arquivo xml plano para uma tabela vazia do livro de visitas ficaria parecido com isso:

<?xml version="1.0" ?>
<dataset>
    <guestbook />
</dataset>

A manipulação de valores NULL com o xml plano é tedioso. Um valor NULL é diferente de uma string com valor vazio em quase todos os bancos de dados (Oracle é uma exceção), algo difícil de descrever no formato xml plano. Você pode representar um valor NULL omitindo o atributo da especificação da linha. Se nosso livro de visitas vai permitir entradas anônimas representadas por um valor NULL na coluna user, um estado hipotético para a tabela do livro de visitas seria parecido com:

<?xml version="1.0" ?>
<dataset>
    <guestbook id="1" content="Hello buddy!" user="joe" created="2010-04-24 17:15:23" />
    <guestbook id="2" content="I like it!" created="2010-04-26 12:14:20" />
</dataset>

Nesse caso a segunda entrada é postada anonimamente. Porém isso acarreta um problema sério no reconhecimento de colunas. Durante as asserções de igualdade do conjunto de dados, cada conjunto de dados tem que especificar quais colunas uma tabela possui. Se um atributo for NULL para todas as linhas de uma tabela de dados, como a Extensão para Banco de Dados vai saber que a coluna deve ser parte da tabela?

O conjunto de dados em xml plano faz uma presunção crucial agora, definindo que os atributos na primeira linha definida de uma tabela define as colunas dessa tabela. No exemplo anterior isso significaria que id, content, user e created são colunas da tabela guestbook. Para a segunda linha, onde user não está definido, um NULL seria inserido no banco de dados.

Quando a primeira entrada do guestbook for apagada do conjunto de dados, apenas id, content e created seriam colunas da tabela guestbook, já que user não é especificado.

Para usar o Conjunto de Dados em XML Plano efetivamente, quando valores NULL forem relevantes, a primeira linha de cada tabela não deve conter qualquer valor NULL e apenas as linhas seguintes poderão omitir atributos. Isso pode parecer estranho, já que a ordem das linhas é um fator relevante para as asserções com bancos de dados.

Em troca, se você especificar apenas um subconjunto das colunas da tabela no Conjunto de Dados do XML Plano, todos os valores omitidos serão definidos para seus valores padrão. Isso vai induzir a erros se uma das colunas omitidas estiver definida como NOT NULL DEFAULT NULL.

Para concluir eu posso dizer que os conjuntos de dados XML Plano só devem ser usadas se você não precisar de valores NULL.

Você pode criar uma instância de conjunto de dados xml plano de dentro de seu Caso de Teste de Banco de Dados chamando o método createFlatXmlDataSet($filename):

<?php
class MyTestCase extends PHPUnit_Extensions_Database_TestCase
{
    public function getDataSet()
    {
        return $this->createFlatXmlDataSet('myFlatXmlFixture.xml');
    }
}
?>

Conjunto de Dados XML

Existe um outro Conjunto de Dados em XML mais estruturado, que é um pouco mais verboso para escrever mas evita os problemas do NULL nos conjuntos de dados em XML Plano. Dentro do nó-raiz <dataset> você pode especificar as tags <table>, <column>, <row>, <value> e <null />. Um Conjunto de Dados equivalente ao definido anteriormente no Guestbook em XML Plano seria como:

<?xml version="1.0" ?>
<dataset>
    <table name="guestbook">
        <column>id</column>
        <column>content</column>
        <column>user</column>
        <column>created</column>
        <row>
            <value>1</value>
            <value>Hello buddy!</value>
            <value>joe</value>
            <value>2010-04-24 17:15:23</value>
        </row>
        <row>
            <value>2</value>
            <value>I like it!</value>
            <null />
            <value>2010-04-26 12:14:20</value>
        </row>
    </table>
</dataset>

Qualquer <table> definida tem um nome e requer uma definição de todas as colunas com seus nomes. Pode conter zero ou qualquer número positivo de elementos <row> aninhados. Não definir nenhum elemento <row> significa que a tabela está vazia. As tags <value> e <null /> têm que ser especificadas na ordem dos elementos fornecidos previamente em <column>. A tag <null /> obviamente significa que o valor é NULL.

Você pode criar uma instância de conjunto de dados xml de dentro de seu Caso de Teste de Banco de Dados chamando o método createXmlDataSet($filename):

<?php
class MyTestCase extends PHPUnit_Extensions_Database_TestCase
{
    public function getDataSet()
    {
        return $this->createXMLDataSet('myXmlFixture.xml');
    }
}
?>

Conjunto de Dados XML MySQL

Este novo formato XML é específico para o servidor de banco de dados MySQL. O suporte para ele foi adicionado no PHPUnit 3.5. Arquivos nesse formato podem ser gerados usando o utilitário mysqldump. Diferente dos conjuntos de dados CSV, que o mysqldump também suporta, um único arquivo neste formato XML pode conter dados para múltiplas tabelas. Você pode criar um arquivo nesse formato invocando o mysqldump desta forma:

mysqldump --xml -t -u [username] --password=[password] [database] > /path/to/file.xml

Esse arquivo pode ser usado em seu Caso de Teste de Banco de Dados chamando o método createMySQLXMLDataSet($filename):

<?php
class MyTestCase extends PHPUnit_Extensions_Database_TestCase
{
    public function getDataSet()
    {
        return $this->createMySQLXMLDataSet('/path/to/file.xml');
    }
}
?>

Conjunto de Dados YAML

Alternativamente, você pode usar o Conjunto de dados YAML para o exemplo guestbook:

guestbook:
  -
    id: 1
    content: "Hello buddy!"
    user: "joe"
    created: 2010-04-24 17:15:23
  -
    id: 2
    content: "I like it!"
    user:
    created: 2010-04-26 12:14:20

Isso é simples, conveniente E resolve o problema do NULL que o Conjunto de Dados similar do XML Plano tem. Um NULL em um YAML é apenas o nome da coluna sem nenhum valor especificado. Uma string vazia é especificada como column1: "".

O Conjunto de Dados YAML atualmente não possui método fábrica no Caso de Teste de Banco de Dados, então você tem que instanciar manualmente:

<?php
class YamlGuestbookTest extends PHPUnit_Extensions_Database_TestCase
{
    protected function getDataSet()
    {
        return new PHPUnit_Extensions_Database_DataSet_YamlDataSet(
            dirname(__FILE__)."/_files/guestbook.yml"
        );
    }
}
?>

Conjunto de Dados CSV

Outro Conjunto de Dados baseado em arquivo, com arquivos CSV. Cada tabela do conjunto de dados é representada como um único arquivo CSV. Para nosso exemplo do guestbook, vamos definir um arquivo guestbook-table.csv:

id,content,user,created
1,"Hello buddy!","joe","2010-04-24 17:15:23"
2,"I like it!","nancy","2010-04-26 12:14:20"

Apesar disso ser muito conveniente para edição no Excel ou OpenOffice, você não pode especificar valores NULL em um Conjunto de Dados CSV. Uma coluna vazia levaria a um valor vazio padrão de banco de dados a ser inserido na coluna.

Você pode criar um Conjunto de Dados CSV chamando:

<?php
class CsvGuestbookTest extends PHPUnit_Extensions_Database_TestCase
{
    protected function getDataSet()
    {
        $dataSet = new PHPUnit_Extensions_Database_DataSet_CsvDataSet();
        $dataSet->addTable('guestbook', dirname(__FILE__)."/_files/guestbook.csv");
        return $dataSet;
    }
}
?>

Conjunto de Dados em Vetor

Desde a versão 1.3.2 da Extensão de Banco de dados do PHPUnit há um array baseado em Conjunto de dados. Nosso exemplo do guestbook deveria ficar parecido com:

<?php
class ArrayGuestbookTest extends PHPUnit_Extensions_Database_TestCase
{
    protected function getDataSet()
    {
        return $this->createArrayDataSet(array(
            'guestbook' => array(
                array('id' => 1, 'content' => 'Hello buddy!', 'user' => 'joe', 'created' => '2010-04-24 17:15:23'),
                array('id' => 2, 'content' => 'I like it!',   'user' => null,  'created' => '2010-04-26 12:14:20'),
            ),
        ));
    }
}
?>

Um Conjunto de Dados do PHP tem algumas vantagens óbvias sobre todas os outros conjuntos de dados baseados em arquivos:

  • Vetores PHP podem, obviamente, trabalhar com valores NULL.

  • Você não precisa de arquivos adicionais para asserções e pode especificá-las diretamente no Caso de Teste.

Para este Conjunto de Dados, como nos Conjuntos de Dados em XML Plano, CSV e YAML, as chaves da primeira linha especificada definem os nomes das colunas das tabelas, que no caso anterior seriam id, content, user e created.

Conjunto de Dados Query (SQL)

Para asserções de banco de dados você não precisa somente de conjuntos de dados baseados em arquivos, mas também de conjuntos de dados baseados em Query/SQL que contenha os conteúdos reais do banco de dados. É aí que entra o Query DataSet:

<?php
$ds = new PHPUnit_Extensions_Database_DataSet_QueryDataSet($this->getConnection());
$ds->addTable('guestbook');
?>

Adicionar uma tabela apenas por nome é um modo implícito de definir a tabela de dados com a seguinte query:

<?php
$ds = new PHPUnit_Extensions_Database_DataSet_QueryDataSet($this->getConnection());
$ds->addTable('guestbook', 'SELECT * FROM guestbook');
?>

Você pode fazer uso disso especificando queries arbitrárias para suas tabelas, por exemplo restringindo linhas, colunas, ou adicionando cláusulas ORDER BY:

<?php
$ds = new PHPUnit_Extensions_Database_DataSet_QueryDataSet($this->getConnection());
$ds->addTable('guestbook', 'SELECT id, content FROM guestbook ORDER BY created DESC');
?>

A seção nas Asserções de Banco de Dados mostrará mais alguns detalhes sobre como fazer uso do Conjunto de Dados Query.

Conjunto de Dados de Banco de Dados (BD)

Acessando a Conexão de Teste você pode criar automaticamente um Conjunto de Dados que consiste de todas as tabelas com seus conteúdos no banco de dados especificado como segundo parâmetro ao método Fábrica de Conexões.

Você pode tanto criar um Conjunto de Dados para todo o banco de dados como mostrado em testGuestbook(), ou restringi-lo a um conjunto de nomes específicos de tabelas com uma lista branca, como mostrado no método testFilteredGuestbook().

<?php
class MySqlGuestbookTest extends PHPUnit_Extensions_Database_TestCase
{
    /**
     * @return PHPUnit_Extensions_Database_DB_IDatabaseConnection
     */
    public function getConnection()
    {
        $database = 'my_database';
        $user = 'my_user';
        $password = 'my_password';
        $pdo = new PDO('mysql:...', $user, $password);
        return $this->createDefaultDBConnection($pdo, $database);
    }

    public function testGuestbook()
    {
        $dataSet = $this->getConnection()->createDataSet();
        // ...
    }

    public function testFilteredGuestbook()
    {
        $tableNames = array('guestbook');
        $dataSet = $this->getConnection()->createDataSet($tableNames);
        // ...
    }
}
?>

Conjunto de Dados de Reposição

Eu tenho falado sobre problemas com NULL nos Conjunto de Dados XML Plano e CSV, mas existe uma alternativa um pouco complicada para fazer ambos funcionarem com NULLs.

O Conjunto de Dados de Reposição é um decorador para um Conjunto de Dados existente e permite que você substitua valores em qualquer coluna do conjunto de dados por outro valor de reposição. Para fazer nosso exemplo do guestbook funcionar com valores NULL devemos especificar o arquivo como:

<?xml version="1.0" ?>
<dataset>
    <guestbook id="1" content="Hello buddy!" user="joe" created="2010-04-24 17:15:23" />
    <guestbook id="2" content="I like it!" user="##NULL##" created="2010-04-26 12:14:20" />
</dataset>

Então envolvemos o Conjunto de Dados em XML Plano dentro de um Conjunto de Dados de Reposição:

<?php
class ReplacementTest extends PHPUnit_Extensions_Database_TestCase
{
    public function getDataSet()
    {
        $ds = $this->createFlatXmlDataSet('myFlatXmlFixture.xml');
        $rds = new PHPUnit_Extensions_Database_DataSet_ReplacementDataSet($ds);
        $rds->addFullReplacement('##NULL##', null);
        return $rds;
    }
}
?>

Filtro de Conjunto de Dados

Se você tiver um arquivo grande de ambiente você pode usar o Filtro de Conjunto de Dados para as listas branca e negra das tabelas e colunas que deveriam estar contidas em um sub-conjunto de dados. Isso ajuda especialmente em combinação com o DB DataSet para filtrar as colunas dos conjuntos de dados.

<?php
class DataSetFilterTest extends PHPUnit_Extensions_Database_TestCase
{
    public function testIncludeFilteredGuestbook()
    {
        $tableNames = array('guestbook');
        $dataSet = $this->getConnection()->createDataSet();

        $filterDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($dataSet);
        $filterDataSet->addIncludeTables(array('guestbook'));
        $filterDataSet->setIncludeColumnsForTable('guestbook', array('id', 'content'));
        // ..
    }

    public function testExcludeFilteredGuestbook()
    {
        $tableNames = array('guestbook');
        $dataSet = $this->getConnection()->createDataSet();

        $filterDataSet = new PHPUnit_Extensions_Database_DataSet_DataSetFilter($dataSet);
        $filterDataSet->addExcludeTables(array('foo', 'bar', 'baz')); // only keep the guestbook table!
        $filterDataSet->setExcludeColumnsForTable('guestbook', array('user', 'created'));
        // ..
    }
}
?>

NOTA Você não pode usar ambos os filtros de coluna excluir e incluir na mesma tabela, apenas em tabelas diferentes. E mais: só é possível para a lista branca ou negra, mas não para ambas.

Conjunto de Dados Composto

O Conjunto de Dados composto é muito útil para agregar vários conjuntos de dados já existentes em um único Conjunto de Dados. Quando vários conjuntos de dados contém as mesmas tabelas, as linhas são anexadas na ordem especificada. Por exemplo se tivermos dois conjuntos de dados fixture1.xml:

<?xml version="1.0" ?>
<dataset>
    <guestbook id="1" content="Hello buddy!" user="joe" created="2010-04-24 17:15:23" />
</dataset>

e fixture2.xml:

<?xml version="1.0" ?>
<dataset>
    <guestbook id="2" content="I like it!" user="##NULL##" created="2010-04-26 12:14:20" />
</dataset>

Usando o Conjunto de Dados Composto podemos agregar os dois arquivos de ambiente:

<?php
class CompositeTest extends PHPUnit_Extensions_Database_TestCase
{
    public function getDataSet()
    {
        $ds1 = $this->createFlatXmlDataSet('fixture1.xml');
        $ds2 = $this->createFlatXmlDataSet('fixture2.xml');

        $compositeDs = new PHPUnit_Extensions_Database_DataSet_CompositeDataSet();
        $compositeDs->addDataSet($ds1);
        $compositeDs->addDataSet($ds2);

        return $compositeDs;
    }
}
?>

Cuidado com Chaves Estrangeiras

Durante a Configuração do Ambiente a Extensão para Banco de Dados do PHPUnit insere as linhas no banco de dados na ordem que são especificadas em seu ambiente. Se seu esquema de banco de dados usa chaves estrangeiras isso significa que você tem que especificar as tabelas em uma ordem que não faça as restrições das chaves estrangeiras falharem.

Implementando seus próprios Conjuntos de Dados/ Tabelas de Dados

Para entender os interiores dos Conjuntos de Dados e Tabelas de Dados, vamos dar uma olhada na interface de um Conjunto de Dados. Você pode pular esta parte se você não planeja implementar seu próprio Conjunto de Dados ou Tabela de Dados.

<?php
interface PHPUnit_Extensions_Database_DataSet_IDataSet extends IteratorAggregate
{
    public function getTableNames();
    public function getTableMetaData($tableName);
    public function getTable($tableName);
    public function assertEquals(PHPUnit_Extensions_Database_DataSet_IDataSet $other);

    public function getReverseIterator();
}
?>

A interface pública é usada internamente pela asserção assertDataSetsEqual() no Caso de Teste de Banco de Dados para verificar a qualidade do conjunto de dados. Da interface IteratorAggregate o IDataSet herda o método getIterator() para iterar sobre todas as tabelas do conjunto de dados. O iterador reverso permite o PHPUnit truncar corretamente as tabelas em ordem reversa à que foi criada para satisfazer as restrições de chaves estrangeiras.

Dependendo da implementação, diferentes abordagens são usadas para adicionar instâncias de tabela a um Conjunto de Dados. Por exemplo, tabelas são adicionadas internamente durante a construção a partir de um arquivo fonte em todos os conjuntos de dados baseados em arquivo como YamlDataSet, XmlDataSet ou FlatXmlDataSet.

Uma tabela também é representada pela seguinte interface:

<?php
interface PHPUnit_Extensions_Database_DataSet_ITable
{
    public function getTableMetaData();
    public function getRowCount();
    public function getValue($row, $column);
    public function getRow($row);
    public function assertEquals(PHPUnit_Extensions_Database_DataSet_ITable $other);
}
?>

Com exceção do método getTableMetaData() isso é bastante auto-explicativo. Os métodos usados são todos requeridos para as diferentes asserções da Extensão para Banco de Dados que são explicados no próximo capítulo. O método getTableMetaData() deve retornar uma implementação da interface PHPUnit_Extensions_Database_DataSet_ITableMetaData que descreve a estrutura da tabela. Possui informações sobre:

  • O nome da tabela

  • Um vetor de nomes de coluna da tabela, ordenado por suas aparições nos conjuntos de resultados.

  • Um vetor de colunas de chaves-primárias.

Essa interface também tem uma asserção que verifica se duas instâncias de Metadados de Tabela se equivalem, o que é usado pela asserção de igualdade do conjunto de dados.

A API de Conexão

Existem três métodos interessantes na interface Connection que devem ser retornados do método getConnection() no Caso de Teste de Banco de Dados:

<?php
interface PHPUnit_Extensions_Database_DB_IDatabaseConnection
{
    public function createDataSet(Array $tableNames = NULL);
    public function createQueryTable($resultName, $sql);
    public function getRowCount($tableName, $whereClause = NULL);

    // ...
}
?>
  1. O método createDataSet() cria um Conjunto de Dados de Banco de Dados (BD) como descrito na seção de implementações de Conjunto de Dados.

    <?php
    class ConnectionTest extends PHPUnit_Extensions_Database_TestCase
    {
        public function testCreateDataSet()
        {
            $tableNames = array('guestbook');
            $dataSet = $this->getConnection()->createDataSet();
        }
    }
    ?>
  2. O método createQueryTable() pode ser usado para criar instâncias de uma QueryTable, dado um nome de resultado e uma query SQL. Este é um método conveniente quando se fala sobre asserções de resultado/tabela como será mostrado na próxima seção de API de Asserções de Banco de Dados.

    <?php
    class ConnectionTest extends PHPUnit_Extensions_Database_TestCase
    {
        public function testCreateQueryTable()
        {
            $tableNames = array('guestbook');
            $queryTable = $this->getConnection()->createQueryTable('guestbook', 'SELECT * FROM guestbook');
        }
    }
    ?>
  3. O método getRowCount() é uma forma conveniente de acessar o número de linhas em uma tabela, opcionalmente filtradas por uma cláusula where adicional. Isso pode ser usado com uma simples asserção de igualdade:

    <?php
    class ConnectionTest extends PHPUnit_Extensions_Database_TestCase
    {
        public function testGetRowCount()
        {
            $this->assertEquals(2, $this->getConnection()->getRowCount('guestbook'));
        }
    }
    ?>

API de Asserções de Banco de Dados

Para uma ferramenta de testes, a Extensão para Banco de Dados certamente fornece algumas asserções que você pode usar para verificar o estado atual do banco de dados, tabelas e a contagem de linhas de tabelas. Esta seção descreve essa funcionalidade em detalhes:

Asseverado a contagem de linhas de uma Tabela

Às vezes ajuda verificar se uma tabela contém uma quantidade específica de linhas. Você pode conseguir isso facilmente sem colar códigos adicionais usando a API de Conexão. Suponha que queiramos verificar se após a inserção de uma linha em nosso guestbook não apenas temos as duas entradas iniciais que nos acompanharam em todos os exemplos anteriores, mas uma terceira:

<?php
class GuestbookTest extends PHPUnit_Extensions_Database_TestCase
{
    public function testAddEntry()
    {
        $this->assertEquals(2, $this->getConnection()->getRowCount('guestbook'), "Pre-Condition");

        $guestbook = new Guestbook();
        $guestbook->addEntry("suzy", "Hello world!");

        $this->assertEquals(3, $this->getConnection()->getRowCount('guestbook'), "Inserting failed");
    }
}
?>

Asseverando o Estado de uma Tabela

A asserção anterior ajuda, mas certamente queremos verificar os conteúdos reais da tabela para verificar se todos os valores foram escritos nas colunas corretas. Isso pode ser conseguido com uma asserção de tabela.

Para isso vamos definir uma instância de Tabela Query que deriva seu conteúdo de um nome de tabela e de uma query SQL e compara isso a um Conjunto de Dados baseado em Arquivo/Vetor:

<?php
class GuestbookTest extends PHPUnit_Extensions_Database_TestCase
{
    public function testAddEntry()
    {
        $guestbook = new Guestbook();
        $guestbook->addEntry("suzy", "Hello world!");

        $queryTable = $this->getConnection()->createQueryTable(
            'guestbook', 'SELECT * FROM guestbook'
        );
        $expectedTable = $this->createFlatXmlDataSet("expectedBook.xml")
                              ->getTable("guestbook");
        $this->assertTablesEqual($expectedTable, $queryTable);
    }
}
?>

Agora temos que escrever o arquivo XML Plano expectedBook.xml para esta asserção:

<?xml version="1.0" ?>
<dataset>
    <guestbook id="1" content="Hello buddy!" user="joe" created="2010-04-24 17:15:23" />
    <guestbook id="2" content="I like it!" user="nancy" created="2010-04-26 12:14:20" />
    <guestbook id="3" content="Hello world!" user="suzy" created="2010-05-01 21:47:08" />
</dataset>

Apesar disso, esta asserção só vai passar em exatamente um segundo do universo, em 2010–05–01 21:47:08. Datas possuem um problema especial nos testes de bancos de dados e podemos circundar a falha omitindo a coluna created da asserção.

O arquivo ajustado expectedBook.xml em XML Plano provavelmente vai ficar parecido com o seguinte para fazer a asserção passar:

<?xml version="1.0" ?>
<dataset>
    <guestbook id="1" content="Hello buddy!" user="joe" />
    <guestbook id="2" content="I like it!" user="nancy" />
    <guestbook id="3" content="Hello world!" user="suzy" />
</dataset>

Nós temos que consertar a chamada da Tabela Query:

<?php
$queryTable = $this->getConnection()->createQueryTable(
    'guestbook', 'SELECT id, content, user FROM guestbook'
);
?>

Asseverando o Resultado de uma Query

Você também pode asseverar o resultado de querys complexas com a abordagem da Tabela Query, apenas especificando um nome de resultado com uma query e comparando isso a um conjunto de dados:

<?php
class ComplexQueryTest extends PHPUnit_Extensions_Database_TestCase
{
    public function testComplexQuery()
    {
        $queryTable = $this->getConnection()->createQueryTable(
            'myComplexQuery', 'SELECT complexQuery...'
        );
        $expectedTable = $this->createFlatXmlDataSet("complexQueryAssertion.xml")
                              ->getTable("myComplexQuery");
        $this->assertTablesEqual($expectedTable, $queryTable);
    }
}
?>

Asseverando o Estado de Múltiplas Tabelas

Certamente você pode asseverar o estado de múltiplas tabelas de uma vez e comparar um conjunto de dados de query contra um conjunto de dados baseado em arquivo. Existem duas formas diferentes de asserções de Conjuntos de Dados.

  1. Você pode usar o Database (Banco de Dados - DB) e o DataSet (Conjunto de Dados) da Connection (Conexão) e compará-lo com um Conjunto de Dados Baseado em Arquivo.

    <?php
    class DataSetAssertionsTest extends PHPUnit_Extensions_Database_TestCase
    {
        public function testCreateDataSetAssertion()
        {
            $dataSet = $this->getConnection()->createDataSet(array('guestbook'));
            $expectedDataSet = $this->createFlatXmlDataSet('guestbook.xml');
            $this->assertDataSetsEqual($expectedDataSet, $dataSet);
        }
    }
    ?>
  2. Você pode construir o Conjunto de Dados por si próprio:

    <?php
    class DataSetAssertionsTest extends PHPUnit_Extensions_Database_TestCase
    {
        public function testManualDataSetAssertion()
        {
            $dataSet = new PHPUnit_Extensions_Database_DataSet_QueryDataSet();
            $dataSet->addTable('guestbook', 'SELECT id, content, user FROM guestbook'); // additional tables
            $expectedDataSet = $this->createFlatXmlDataSet('guestbook.xml');
    
            $this->assertDataSetsEqual($expectedDataSet, $dataSet);
        }
    }
    ?>

Perguntas Mais Frequentes

O PHPUnit vai (re)criar o esquema do banco de dados para cada teste?

Não, o PHPUnit exige que todos os objetos do banco de dados estejam disponíveis quando a suíte começar os testes. O Banco de Dados, tabelas, sequências, gatilhos e visualizações devem ser criadas antes que você execute a suíte de testes.

Doctrine 2 ou eZ Components possuem ferramentas poderosas que permitem a você criar o esquema de banco de dados através de estruturas de dados pré-definidas, porém devem ser ligados à extensão do PHPUnit para permitir a recriação automática de banco de dados antes que a suíte de testes completa seja executada.

Já que cada teste limpa completamente o banco de dados, você nem sequer é forçado a recriar o banco de dados para cada execução de teste. Um banco de dados permanentemente disponível funciona perfeitamente.

Sou forçado a usar PDO em minha aplicação para que a Extensão para Banco de Dados funcione?

Não, PDO só é exigido para limpeza e configuração do ambiente e para asserções. Você pode usar qualquer abstração de banco de dados que quiser dentro de seu próprio código.

O que posso fazer quando recebo um Erro Too much Connections?

Se você não armazena em cache a instância de PDO que é criada a partir do método do Caso de Teste getConnection() o número de conexões ao banco de dados é aumentado em um ou mais com cada teste do banco de dados. Com a configuração padrão o MySQL só permite 100 conexões concorrentes e outros fornecedores também têm um limite máximo de conexões.

A Sub-seção Use seu próprio Caso Abstrato de Teste de Banco de Dados mostra como você pode prevenir o acontecimento desse erro usando uma instância única armazenada em cache do PDO em todos os seus testes.

Como lidar com NULL usando Conjuntos de Dados XML Plano / CSV?

Não faça isso. Em vez disso, você deveria usar Conjuntos de Dados ou XML ou YAML.

Capítulo 9. Dublês de Testes

Gerard Meszaros introduz o conceito de Dublês de Testes em [Meszaros2007] desta forma:

 

Às vezes é muito difícil testar o sistema sob teste (SST - em inglês: system under test - SUT) porque isso depende de outros ambientes que não podem ser usados no ambiente de testes. Isso pode ser porque não estão disponíveis, não retornarão os resultados necessários para o teste, ou porque executá-los causaria efeitos colaterais indesejáveis. Em outros casos, nossa estratégia de testes requer que tenhamos mais controle ou visibilidade do comportamento interno do SST.

Quando estamos escrevendo um teste no qual não podemos (ou decidimos não) usar um componente dependente (depended-on component - DOC) real, podemos substitui-lo por um Dublê de Teste. O Dublê de Teste não precisa se comportar exatamente como o DOC real; apenas precisa fornecer a mesma API como o real, de forma que o SST pense que é o real!

 
 --Gerard Meszaros

O método getMockBuilder($type) fornecido pelo PHPUnit pode ser usado em um teste para gerar automaticamente um objeto que possa atuar como um dublê de teste para a classe original especificada. Esse objeto de dublê de teste pode ser usado em cada contexto onde um objeto da classe original é esperado ou requerido.

Por padrão, todos os métodos da classe original são substituídos com uma implementação simulada que apenas retorna null (sem chamar o método original). Usando o método will($this->returnValue()), por exemplo, você pode configurar essas implementações simuladas para retornar um valor quando chamadas.

Limitações

Por favor, note que os métodos final, private e static não podem ser esboçados (stubbed) ou falsificados (mocked). Eles são ignorados pela funcionalidade de dublê de teste do PHPUnit e mantêm seus comportamentos originais.

Aviso

Por favor atente para o fato de que a gestão de parâmetros foi mudada. A implementação anterior clona todos os parâmetros de objetos. Isso não permite verificar se o mesmo objeto foi passado para um método ou não. Exemplo 9.15 mostra onde a nova implementação pode ser útil. Exemplo 9.16 mostra como voltar para o comportamento anterior.

Esboços (stubs)

A prática de substituir um objeto por um dublê de teste que (opcionalmente) retorna valores de retorno configurados é chamada de delineamento. Você pode usar um esboço para "substituir um componente real do qual o SST depende de modo que o teste tenha um ponto de controle para as entradas indiretas do SST. Isso permite ao teste forçar o SST através de caminhos que não seriam executáveis de outra forma".

Exemplo 9.2 mostra como esboçar chamadas de método e configurar valores de retorno. Primeiro usamos o método getMockBuilder() que é fornecido pela classe PHPUnit_Framework_TestCase para configurar um esboço de objeto que parece com um objeto de SomeClass (Exemplo 9.1). Então usamos a Interface Fluente que o PHPUnit fornece para especificar o comportamento para o esboço. Essencialmente, isso significa que você não precisa criar vários objetos temporários e uni-los depois. Em vez disso, você encadeia chamadas de método como mostrado no exemplo. Isso leva a códigos mais legíveis e "fluentes".

Exemplo 9.1: A classe que queremos esboçar

<?php
class SomeClass
{
    public function doSomething()
    {
        // Faça algo.
    }
}
?>

Exemplo 9.2: Esboçando uma chamada de método para retornar um valor fixo

<?php
require_once 'SomeClass.php';

class StubTest extends PHPUnit_Framework_TestCase
{
    public function testStub()
    {
        // Cria um esboço para a classe AlgumaClasse.
        $stub = $this->getMockBuilder('SomeClass')
                     ->getMock();

        // Configura o esboço.
        $stub->method('doSomething')
             ->willReturn('foo');

        // Chamando $esboco->fazAlgumaCoisa() agora vai retornar 
        // 'foo'.
        $this->assertEquals('foo', $stub->doSomething());
    }
}
?>

"Atrás dos bastidores" o PHPUnit automaticamente gera uma nova classe PHP que implementa o comportamento desejado quando o método getMock() é usado.

Exemplo 9.3 mostra um exemplo de como usar a interface fluente do Mock Builder para configurar a criação do dublê de teste.

Exemplo 9.3: Usando a API Mock Builder pode ser usada para configurar a classe de dublê de teste gerada

<?php
require_once 'SomeClass.php';

class StubTest extends PHPUnit_Framework_TestCase
{
    public function testStub()
    {
        // Cria um esboço para a classe SomeClass.
        $stub = $this->getMockBuilder('SomeClass')
                     ->disableOriginalConstructor()
                     ->getMock();

        // Configura o esboço.
        $stub->method('doSomething')
             ->willReturn('foo');

        // Chamar $stub->doSomething() agora vai retornar
        // 'foo'.
        $this->assertEquals('foo', $stub->doSomething());
    }
}
?>

Aqui está uma lista de métodos fornecidos pelo Mock Builder:

  • setMethods(array $methods) pode ser chamado no objeto Mock Builder para especificar os métodos que devem ser substituídos com um dublê de teste configurável. O comportamento dos outros métodos não muda. Se você chamar setMethods(null), então nenhum dos métodos serão substituídos.

  • setConstructorArgs(array $args) pode ser chamado para fornecer um vetor de parâmetros que é passado ao construtor da classe original (que por padrão não é substituído com uma implementação falsa).

  • setMockClassName($name) pode ser usado para especificar um nome de classe para a classe de dublê de teste gerada.

  • disableOriginalConstructor() pode ser usado para desabilitar a chamada ao construtor da classe original.

  • disableOriginalClone() pode ser usado para desabilitar a chamada ao construtor do clone da classe original.

  • disableAutoload() pode ser usado para desabilitar o __autoload() durante a geração da classe de dublê de teste.

Nos exemplos até agora temos retornado valores simples usando willReturn($value). Essa sintaxe curta é o mesmo que will($this->returnValue($value)). Podemos usar variações desta sintaxe longa para alcançar mais comportamento de esboço complexo.

Às vezes você quer retornar um dos argumentos de uma chamada de método (inalterada) como o resultado de uma chamada ao método esboçado. Exemplo 9.4 mostra como você pode conseguir isso usando returnArgument() em vez de returnValue().

Exemplo 9.4: Esboçando uma chamada de método para retornar um dos argumentos

<?php
require_once 'SomeClass.php';

class StubTest extends PHPUnit_Framework_TestCase
{
    public function testReturnArgumentStub()
    {
        // Cria um esboço para a classe SomeClass.
        $stub = $this->getMockBuilder('SomeClass')
                     ->getMock();

        // Configura o esboço.
        $stub->method('doSomething')
             ->will($this->returnArgument(0));

        // $stub->doSomething('foo') retorna 'foo'.
        $this->assertEquals('foo', $stub->doSomething('foo'));

        // $stub->doSomething('bar') retorna 'bar'.
        $this->assertEquals('bar', $stub->doSomething('bar'));
    }
}
?>

Ao testar uma interface fluente, às vezes é útil fazer um método esboçado retornar uma referência ao objeto esboçado. Exemplo 9.5 mostra como você pode usar returnSelf() para conseguir isso.

Exemplo 9.5: Esboçando uma chamada de método para retornar uma referência ao objeto esboçado

<?php
require_once 'SomeClass.php';

class StubTest extends PHPUnit_Framework_TestCase
{
    public function testReturnSelf()
    {
        // Cria um esboço para a classe SomeClass.
        $stub = $this->getMockBuilder('SomeClass')
                     ->getMock();

        // Configura o esboço.
        $stub->method('doSomething')
             ->will($this->returnSelf());

        // $stub->doSomething() retorna $stub
        $this->assertSame($stub, $stub->doSomething());
    }
}
?>

Algumas vezes um método esboçado deveria retornar valores diferentes dependendo de uma lista predefinida de argumentos. Você pode usar returnValueMap() para criar um mapa que associa argumentos com valores de retorno correspondentes. Veja Exemplo 9.6 para ter um exemplo.

Exemplo 9.6: Esboçando uma chamada de método para retornar o valor de um mapa

<?php
require_once 'SomeClass.php';

class StubTest extends PHPUnit_Framework_TestCase
{
    public function testReturnValueMapStub()
    {
        // Cria um esboço para a classe SomeClass.
        $stub = $this->getMockBuilder('SomeClass')
                     ->getMock();

        // Cria um mapa de argumentos para valores retornados.
        $map = array(
          array('a', 'b', 'c', 'd'),
          array('e', 'f', 'g', 'h')
        );

        // Configura o esboço.
        $stub->method('doSomething')
             ->will($this->returnValueMap($map));

        // $stub->doSomething() retorna diferentes valores dependendo do 
        // argumento fornecido.
        $this->assertEquals('d', $stub->doSomething('a', 'b', 'c'));
        $this->assertEquals('h', $stub->doSomething('e', 'f', 'g'));
    }
}
?>

Quando a chamada ao método esboçado deve retornar um valor calculado em vez de um fixo (veja returnValue()) ou um argumento (inalterado) (veja returnArgument()), você pode usar returnCallback() para que o método esboçado retorne o resultado da função ou método callback. Veja Exemplo 9.7 para ter um exemplo.

Exemplo 9.7: Esboçando uma chamada de método para retornar um valor de um callback

<?php
require_once 'SomeClass.php';

class StubTest extends PHPUnit_Framework_TestCase
{
    public function testReturnCallbackStub()
    {
        // Cria um esboço para a classe AlgumaClasse.
        $stub = $this->getMockBuilder('SomeClass')
                     ->getMock();

        // Configura o esboço.
        $stub->method('doSomething')
             ->will($this->returnCallback('str_rot13'));

        // $stub->doSomething($argument) retorna str_rot13($argument)
        $this->assertEquals('fbzrguvat', $stub->doSomething('something'));
    }
}
?>

Uma alternativa mais simples para configurar um método callback pode ser especificar uma lista de valores de retorno desejados. Você pode fazer isso com o método onConsecutiveCalls(). Veja Exemplo 9.8 para ter um exemplo.

Exemplo 9.8: Esboçando uma chamada de método para retornar uma lista de valores na ordem especificada

<?php
require_once 'SomeClass.php';

class StubTest extends PHPUnit_Framework_TestCase
{
    public function testOnConsecutiveCallsStub()
    {
        // Cria um esboço para a classe SomeClass.
        $stub = $this->getMockBuilder('SomeClass')
                     ->getMock();

        // Configura o esboço.
        $stub->method('doSomething')
             ->will($this->onConsecutiveCalls(2, 3, 5, 7));

        // $stub->doSomething() retorna um valor diferente em cada vez
        $this->assertEquals(2, $stub->doSomething());
        $this->assertEquals(3, $stub->doSomething());
        $this->assertEquals(5, $stub->doSomething());
    }
}
?>

Em vez de retornar um valor, um método esboçado também pode causar uma exceção. Exemplo 9.9 mostra como usar throwException() para fazer isso.

Exemplo 9.9: Esboçando uma chamada de método para lançar uma exceção

<?php
require_once 'SomeClass.php';

class StubTest extends PHPUnit_Framework_TestCase
{
    public function testThrowExceptionStub()
    {
        // Cria um esboço para a classe SomeClass.
        $stub = $this->getMockBuilder('SomeClass')
                     ->getMock();

        // Configura o esboço.
        $stub->method('doSomething')
             ->will($this->throwException(new Exception));

        // $stub->doSomething() lança Exceção
        $stub->doSomething();
    }
}
?>

Alternativamente, você mesmo pode escrever um esboço enquanto melhora o design. Recursos amplamente utilizados são acessados através de uma única fachada, então você pode substituir facilmente o recurso pelo esboço. Por exemplo, em vez de ter chamadas diretas ao banco de dados espalhadas pelo código, você tem um único objeto Database que implementa a interface IDatabase. Então, você pode criar um esboço de implementação da IDatabase e usá-la em seus testes. Você pode até criar uma opção para executar os testes com o esboço do banco de dados ou com o banco de dados real, então você pode usar seus testes tanto para testes locais durante o desenvolvimento quanto para integração dos testes com o banco de dados real.

Funcionalidades que precisam ser esboçadas tendem a se agrupar no mesmo objeto, aumentando a coesão. Por apresentar a funcionalidade com uma interface única e coerente, você reduz o acoplamento com o resto do sistema.

Objetos Falsos

A prática de substituir um objeto por um dublê de teste que verifica expectativas, por exemplo asseverando que um método foi chamado, é conhecido como falsificação (mocking).

Você pode usar um objeto falso "como um ponto de observação que é usado para verificar as saídas indiretas do SST durante seu exercício. Tipicamente, o objeto falso também inclui a funcionalidade de um esboço de teste que deve retornar valores para o SST se ainda não tiver falhado nos testes, mas a ênfase está na verificação das saídas indiretas. Portanto, um objeto falso é muito mais que apenas um esboço de testes mais asserções; é utilizado de uma forma fundamentalmente diferente".

Limitações

Somente objetos falsos gerados no escopo de um teste irá ser verificado automaticamente pelo PHPUnit. Objetos falsos gerados em provedores de dados, por exemplo, não serão verificados pelo PHPUnit.

Aqui está um exemplo: suponha que queiramos testar se o método correto, update() em nosso exemplo, é chamado em um objeto que observa outro objeto. Exemplo 9.10 mostra o código para as classes Subject e Observer que são parte do Sistema Sob Teste (SST).

Exemplo 9.10: As classes Subject e Observer que são parte do Sistema Sob Teste (SST)

<?php
class Subject
{
    protected $observers = array();
    protected $name;

    public function __construct($name)
    {
        $this->name = $name;
    }

    public function getName()
    {
        return $this->name;
    }

    public function attach(Observer $observer)
    {
        $this->observers[] = $observer;
    }

    public function doSomething()
    {
        // Faça algo.

        // ...

        // Notifica aos observadores que fizemos algo.
        $this->notify('something');
    }

    public function doSomethingBad()
    {
        foreach ($this->observers as $observer) {
            $observer->reportError(42, 'Something bad happened', $this);
        }
    }

    protected function notify($argument)
    {
        foreach ($this->observers as $observer) {
            $observer->update($argument);
        }
    }

    // Outros métodos.
}

class Observer
{
    public function update($argument)
    {
        // Faça algo.
    }

    public function reportError($errorCode, $errorMessage, Subject $subject)
    {
        // Faça algo.
    }

    // Outros métodos.
}
?>

Exemplo 9.11 mostra como usar um objeto falso para testar a interação entre os objetos Subject e Observer.

Primeiro usamos o método getMock() que é fornecido pela classe PHPUnit_Framework_TestCase para configurar um objeto falso para ser o Observer. Já que fornecemos um vetor como segundo parâmetro (opcional) para o método getMock(), apenas o método update() da classe Observer é substituído por uma implementação falsificada.

Porque estamos interessados em verificar se um método foi chamado, e com quais argumentos ele foi chamado, introduzimos os métodos expects() e with() para especificar como essa interação deve considerar.

Exemplo 9.11: Testando se um método é chamado uma vez e com o argumento especificado

<?php
class SubjectTest extends PHPUnit_Framework_TestCase
{
    public function testObserversAreUpdated()
    {
        // Cria uma falsificação para a classe Observer,
        // apenas falsificando o método update().
        $observer = $this->getMockBuilder('Observer')
                         ->setMethods(array('update'))
                         ->getMock();

        // Configura a expectativa para o método update()
        // para ser chamado apenas uma vez e com a string 'something'
        // como seu parâmetro.
        $observer->expects($this->once())
                 ->method('update')
                 ->with($this->equalTo('something'));

        // Cria um objeto Subject e anexa a ele o objeto
        // Observer falsificado.
        $subject = new Subject('My subject');
        $subject->attach($observer);

        // Chama o método doSomething() no objeto $subject
        // no qual esperamos chamar o método update()
        // do objeto falsificado Observer, com a string 'something'.
        $subject->doSomething();
    }
}
?>

O método with() pode receber qualquer número de argumentos, correspondendo ao número de argumentos sendo falsos. Você pode especificar restrições mais avançadas do que uma simples igualdade no argumento do método.

Exemplo 9.12: Testando se um método é chamado com um número de argumentos restringidos de formas diferentes

<?php
class SubjectTest extends PHPUnit_Framework_TestCase
{
    public function testErrorReported()
    {
        // Create a mock for the Observer class, mocking the
        // reportError() method
        $observer = $this->getMockBuilder('Observer')
                         ->setMethods(array('reportError'))
                         ->getMock();

        $observer->expects($this->once())
                 ->method('reportError')
                 ->with(
                       $this->greaterThan(0),
                       $this->stringContains('Something'),
                       $this->anything()
                   );

        $subject = new Subject('My subject');
        $subject->attach($observer);

        // The doSomethingBad() method should report an error to the observer
        // via the reportError() method
        $subject->doSomethingBad();
    }
}
?>

O método withConsecutive() pode receber qualquer número de vetores de argumentos, dependendo das chamados que você quer testar contra. Cada vetor é uma lista de restrições correspondentes para os argumentos do método falsificado, como em with().

Exemplo 9.13: Testar que um método foi chamado duas vezes com argumentos especificados

<?php
class FooTest extends PHPUnit_Framework_TestCase
{
    public function testFunctionCalledTwoTimesWithSpecificArguments()
    {
        $mock = $this->getMockBuilder('stdClass')
                     ->setMethods(array('set'))
                     ->getMock();

        $mock->expects($this->exactly(2))
             ->method('set')
             ->withConsecutive(
                 array($this->equalTo('foo'), $this->greaterThan(0)),
                 array($this->equalTo('bar'), $this->greaterThan(0))
             );

        $mock->set('foo', 21);
        $mock->set('bar', 48);
    }
}
?>

A restrição callback() pode ser usada para verificação de argumento mais complexa. Essa restrição recebe um callback PHP como seu único argumento. O callback PHP receberá o argumento a ser verificado como seu único argumento e deverá retornar TRUE se o argumento passou a verificação e FALSE caso contrário.

Exemplo 9.14: Verificação de argumento mais complexa

<?php
class SubjectTest extends PHPUnit_Framework_TestCase
{
    public function testErrorReported()
    {
        // Create a mock for the Observer class, mocking the
        // reportError() method
        $observer = $this->getMockBuilder('Observer')
                         ->setMethods(array('reportError'))
                         ->getMock();

        $observer->expects($this->once())
                 ->method('reportError')
                 ->with($this->greaterThan(0),
                        $this->stringContains('Something'),
                        $this->callback(function($subject){
                          return is_callable(array($subject, 'getName')) &&
                                 $subject->getName() == 'My subject';
                        }));

        $subject = new Subject('My subject');
        $subject->attach($observer);

        // The doSomethingBad() method should report an error to the observer
        // via the reportError() method
        $subject->doSomethingBad();
    }
}
?>

Exemplo 9.15: Testar se um método foi chamado uma vez e com o objeto idêntico ao que foi passado

<?php
class FooTest extends PHPUnit_Framework_TestCase
{
    public function testIdenticalObjectPassed()
    {
        $expectedObject = new stdClass;

        $mock = $this->getMockBuilder('stdClass')
                     ->setMethods(array('foo'))
                     ->getMock();

        $mock->expects($this->once())
             ->method('foo')
             ->with($this->identicalTo($expectedObject));

        $mock->foo($expectedObject);
    }
}
?>

Exemplo 9.16: Cria um objeto falsificado com clonagem de parâmetros habilitada

<?php
class FooTest extends PHPUnit_Framework_TestCase
{
    public function testIdenticalObjectPassed()
    {
        $cloneArguments = true;

        $mock = $this->getMockBuilder('stdClass')
                     ->enableArgumentCloning()
                     ->getMock();

        // now your mock clones parameters so the identicalTo constraint
        // will fail.
    }
}
?>

Tabela A.1 mostra as restrições que podem ser aplicadas aos argumentos do método e Tabela 9.1 mostra os comparados que estão disponíveis para especificar o número de invocações.

Tabela 9.1. Comparadores

ComparadorSignificado
PHPUnit_Framework_MockObject_Matcher_AnyInvokedCount any()Retorna um comparador que corresponde quando o método que é avaliado for executado zero ou mais vezes.
PHPUnit_Framework_MockObject_Matcher_InvokedCount never()Retorna um comparador que corresponde quando o método que é avaliado nunca for executado.
PHPUnit_Framework_MockObject_Matcher_InvokedAtLeastOnce atLeastOnce()Retorna um comparador que corresponde quando o método que é avaliado for executado pelo menos uma vez.
PHPUnit_Framework_MockObject_Matcher_InvokedCount once()Retorna um comparador que corresponde quando o método que é avaliado for executado exatamente uma vez.
PHPUnit_Framework_MockObject_Matcher_InvokedCount exactly(int $count)Retorna um comparador que corresponde quando o método que é avaliado for executado exatamente $count vezes.
PHPUnit_Framework_MockObject_Matcher_InvokedAtIndex at(int $index)Retorna um comparador que corresponde quando o método que é avaliado for invocado no $index fornecido.

Nota

O parâmetro $index para o comparador at() se refere ao índice, iniciando em zero, em todas invocações de métodos para um objeto falsificado fornecido. Tenha cuidado ao usar este comparador, pois pode levar a testes frágeis que são muito intimamente ligados a detalhes de implementação específicos.

Profecia

Prophecy é um "framework PHP de falsificação de objetos muito poderoso e flexível, porém altamente opcional. Embora inicialmente criado para atender as necessidades do phpspec2, ele é flexível o suficiente para ser usado dentro de qualquer framework de teste por aí, com o mínimo de esforço".

O PHPUnit tem suporte nativo para uso do Prophecy para criar dublês de testes desde a versão 4.5. Exemplo 9.17 mostra como o mesmo teste mostrado no Exemplo 9.11 pode ser expressado usando a filosofia do Prophecy de profecias e revelações:

Exemplo 9.17: Testar que um método foi chamado uma vez e com um argumento específico

<?php
class SubjectTest extends PHPUnit_Framework_TestCase
{
    public function testObserversAreUpdated()
    {
        $subject = new Subject('My subject');

        // Create a prophecy for the Observer class.
        $observer = $this->prophesize('Observer');

        // Set up the expectation for the update() method
        // to be called only once and with the string 'something'
        // as its parameter.
        $observer->update('something')->shouldBeCalled();

        // Reveal the prophecy and attach the mock object
        // to the Subject.
        $subject->attach($observer->reveal());

        // Call the doSomething() method on the $subject object
        // which we expect to call the mocked Observer object's
        // update() method with the string 'something'.
        $subject->doSomething();
    }
}
?>

Por favor, referencie a documentação do Prophecy para mais detalhes sobre como criar, configurar, e usar esboços, espiões, e falsificações usando essa alternativa de framework de dublê de teste.

Falsificando Traits e Classes Abstratas

O método getMockForTrait() retorna um objeto falsificado que usa uma trait especificada. Todos métodos abstratos de uma dada trait são falsificados. Isto permite testar os métodos concretos de uma trait.

Exemplo 9.18: Testando os métodos concretos de uma trait

<?php
trait AbstractTrait
{
    public function concreteMethod()
    {
        return $this->abstractMethod();
    }

    public abstract function abstractMethod();
}

class TraitClassTest extends PHPUnit_Framework_TestCase
{
    public function testConcreteMethod()
    {
        $mock = $this->getMockForTrait('AbstractTrait');

        $mock->expects($this->any())
             ->method('abstractMethod')
             ->will($this->returnValue(TRUE));

        $this->assertTrue($mock->concreteMethod());
    }
}
?>

O método getMockForAbstractClass() retorna um objeto falso para uma classe abstrata. Todos os métodos abstratos da classe abstrata fornecida são falsificados. Isto permite testar os métodos concretos de uma classe abstrata.

Exemplo 9.19: Testando os métodos concretos de uma classe abstrata

<?php
abstract class AbstractClass
{
    public function concreteMethod()
    {
        return $this->abstractMethod();
    }

    public abstract function abstractMethod();
}

class AbstractClassTest extends PHPUnit_Framework_TestCase
{
    public function testConcreteMethod()
    {
        $stub = $this->getMockForAbstractClass('AbstractClass');

        $stub->expects($this->any())
             ->method('abstractMethod')
             ->will($this->returnValue(TRUE));

        $this->assertTrue($stub->concreteMethod());
    }
}
?>

Esboçando e Falsificando Serviços Web

Quando sua aplicação interage com um serviço web você quer testá-lo sem realmente interagir com o serviço web. Para tornar mais fáceis o esboço e falsificação dos serviços web, o getMockFromWsdl() pode ser usado da mesma forma que o getMock() (veja acima). A única diferença é que getMockFromWsdl() retorna um esboço ou falsificação baseado em uma descrição de um serviço web em WSDL e getMock() retorna um esboço ou falsificação baseado em uma classe ou interface PHP.

Exemplo 9.20 mostra como getMockFromWsdl() pode ser usado para esboçar, por exemplo, o serviço web descrito em GoogleSearch.wsdl.

Exemplo 9.20: Esboçando um serviço web

<?php
class GoogleTest extends PHPUnit_Framework_TestCase
{
    public function testSearch()
    {
        $googleSearch = $this->getMockFromWsdl(
          'GoogleSearch.wsdl', 'GoogleSearch'
        );

        $directoryCategory = new stdClass;
        $directoryCategory->fullViewableName = '';
        $directoryCategory->specialEncoding = '';

        $element = new stdClass;
        $element->summary = '';
        $element->URL = 'https://phpunit.de/';
        $element->snippet = '...';
        $element->title = '<b>PHPUnit</b>';
        $element->cachedSize = '11k';
        $element->relatedInformationPresent = TRUE;
        $element->hostName = 'phpunit.de';
        $element->directoryCategory = $directoryCategory;
        $element->directoryTitle = '';

        $result = new stdClass;
        $result->documentFiltering = FALSE;
        $result->searchComments = '';
        $result->estimatedTotalResultsCount = 3.9000;
        $result->estimateIsExact = FALSE;
        $result->resultElements = array($element);
        $result->searchQuery = 'PHPUnit';
        $result->startIndex = 1;
        $result->endIndex = 1;
        $result->searchTips = '';
        $result->directoryCategories = array();
        $result->searchTime = 0.248822;

        $googleSearch->expects($this->any())
                     ->method('doGoogleSearch')
                     ->will($this->returnValue($result));

        /**
         * $googleSearch->doGoogleSearch() will now return a stubbed result and
         * the web service's doGoogleSearch() method will not be invoked.
         */
        $this->assertEquals(
          $result,
          $googleSearch->doGoogleSearch(
            '00000000000000000000000000000000',
            'PHPUnit',
            0,
            1,
            FALSE,
            '',
            FALSE,
            '',
            '',
            ''
          )
        );
    }
}
?>

Esboçando o Sistema de Arquivos

vfsStream é um stream wrapper para um sistema de arquivos virtual que pode ser útil em testes unitários para falsificar um sistema de arquivos real.

Simplesmente adicione a dependência mikey179/vfsStream ao seu arquivo composer.json do projeto se você usa o Composer para gerenciar as dependências do seu projeto. Aqui é um exemplo simplório de um arquivo composer.json que apenas define uma dependência em ambiente de desenvolvimento para o PHPUnit 5.0 e vfsStream:

{
    "require-dev": {
        "phpunit/phpunit": "~4.6",
        "mikey179/vfsStream": "~1"
    }
}

Exemplo 9.21 mostra a classe que interage com o sistema de arquivos.

Exemplo 9.21: Uma classe que interage com um sistema de arquivos

<?php
class Example
{
    protected $id;
    protected $directory;

    public function __construct($id)
    {
        $this->id = $id;
    }

    public function setDirectory($directory)
    {
        $this->directory = $directory . DIRECTORY_SEPARATOR . $this->id;

        if (!file_exists($this->directory)) {
            mkdir($this->directory, 0700, TRUE);
        }
    }
}?>

Sem um sistema de arquivos virtual tal como o vfsStream não poderíamos testar o método setDirectory() isolado de influências externas (veja Exemplo 9.22).

Exemplo 9.22: Testando uma classe que interage com o sistema de arquivos

<?php
require_once 'Example.php';

class ExampleTest extends PHPUnit_Framework_TestCase
{
    protected function setUp()
    {
        if (file_exists(dirname(__FILE__) . '/id')) {
            rmdir(dirname(__FILE__) . '/id');
        }
    }

    public function testDirectoryIsCreated()
    {
        $example = new Example('id');
        $this->assertFalse(file_exists(dirname(__FILE__) . '/id'));

        $example->setDirectory(dirname(__FILE__));
        $this->assertTrue(file_exists(dirname(__FILE__) . '/id'));
    }

    protected function tearDown()
    {
        if (file_exists(dirname(__FILE__) . '/id')) {
            rmdir(dirname(__FILE__) . '/id');
        }
    }
}
?>

A abordagem acima tem várias desvantagens:

  • Assim como um recurso externo, podem haver problemas intermitentes com o sistema de arquivos. Isso deixa os testes, com os quais interage, esquisitos.

  • Nos métodos setUp() e tearDown() temos que assegurar que o diretório não existe antes e depois do teste.

  • Quando a execução do teste termina antes do método tearDown() ser invocado, o diretório permanece no sistema de arquivos.

Exemplo 9.23 mostra como o vfsStream pode ser usado para falsificar o sistema de arquivos em um teste para uma classe que interage com o sistema de arquivos.

Exemplo 9.23: Falsificando o sistema de arquivos em um teste para a classe que interage com o sistema de arquivos

<?php
require_once 'vfsStream/vfsStream.php';
require_once 'Example.php';

class ExampleTest extends PHPUnit_Framework_TestCase
{
    public function setUp()
    {
        vfsStreamWrapper::register();
        vfsStreamWrapper::setRoot(new vfsStreamDirectory('exampleDir'));
    }

    public function testDirectoryIsCreated()
    {
        $example = new Example('id');
        $this->assertFalse(vfsStreamWrapper::getRoot()->hasChild('id'));

        $example->setDirectory(vfsStream::url('exampleDir'));
        $this->assertTrue(vfsStreamWrapper::getRoot()->hasChild('id'));
    }
}
?>

Isso tem várias vantagens:

  • O próprio teste fica mais conciso.

  • O vfsStream concede ao desenvolvedor de testes controle total sobre a aparência do ambiente do sistema de arquivos para o código testado.

  • Já que as operações do sistema de arquivos não operam mais no sistema de arquivos real, operações de limpeza em um método tearDown() não são mais exigidas.

Capítulo 10. Práticas de Teste

 

Você sempre pode escrever mais testes. Porém, você vai descobrir rapidamente que apenas uma fração dos testes que você pode imaginar são realmente úteis. O que você quer é escrever testes que falham mesmo quando você acha que eles deveriam funcionar, ou testes que passam mesmo quando você acha que eles deveria falhar. Outra forma de pensar sobre isso é com relação ao custo/benefício. Você quer escrever testes que lhe darão retorno com informação.

 
 --Erich Gamma

Durante o Desenvolvimento

Quando você precisa fazer uma mudança na estrutura interna do programa em que está trabalhando para torná-lo mais fácil de entender e mais barato de modificar sem alterar seu comportamento visível, uma suíte de testes é inestimável na aplicação das assim chamadas refatorações seguras. De outra forma, você poderia não notar o sistema quebrando enquanto você está cuidando da reestruturação.

As seguintes condições vão ajudá-lo a melhorar o código e design do seu projeto, enquanto usa testes unitários para verificar que os passos de transformação da refatoração são, de fato, preservadores de comportamento e não introduzem erros:

  1. Todos os testes unitários são executados corretamente.

  2. O código comunica seus princípios de design.

  3. O código não contém redundâncias.

  4. O código contém o mínimo número de classes e métodos.

Quando você precisar adicionar novas funcionalidades ao sistema, escreva os testes primeiro. Então, você terá terminado de desenvolver quando os testes executarem. Esta prática será discutida em detalhes no próximo capítulo.

Durante a Depuração

Quando você recebe um relatório de defeito, seu impulso pode ser consertar o defeito o mais rápido possível. A experiência mostra que esse impulso não vai lhe servir bem; parece que o conserto de um defeito acaba causando outro defeito.

Você pode conter esses seus impulsos fazendo o seguinte:

  1. Verifique que você pode reproduzir o defeito.

  2. Encontre a demonstração em menor escala do defeito no código. Por exemplo, se um número aparece incorretamente em uma saída, encontre o objeto que está calculando esse número.

  3. Escreva um teste automatizado que falha agora, mas vai passar quando o defeito for consertado.

  4. Conserte o defeito.

Encontrar a menor reprodução confiável do defeito vai te dar a oportunidade de examinar realmente a causa do defeito. O teste que você escreve vai melhorar as chances de que, quando você consertar o defeito, você realmente tê-lo consertado, porque o novo teste reduz a probabilidade de desfazer o conserto com futuras modificações no código. Todos os testes que você escreveu antes reduzem a probabilidade de causar diferentes problemas inadvertidamente.

 

Testes unitários oferecem muitas vantagens:

  • Testar dá aos autores e revisores dos códigos confiança de que remendos produzem os resultados corretos.

  • Criar casos de testes é um bom ímpeto para desenvolvedores descobrirem casos extremos.

  • Testar fornece uma boa maneira de capturar regressões rapidamente, e ter certeza de que nenhuma regressão será repetida duas vezes.

  • Testes unitários fornecem exemplos funcionais de como usar uma API e podem auxiliar significativamente os trabalhos de documentação.

No geral, testes unitários integrados reduzem o custo e o risco de qualquer mudança individual menor. Isso vai permitir o projeto realizar [...] maiores mudanças arquitetônicas [...] rápida e confiavelmente.

 
 --Benjamin Smedberg

Capítulo 11. Análise de Cobertura de Código

 

Na ciência da computação, cobertura de código é um mensuração usada para descrever o grau em que um código fonte de um programa é testado por uma suíte de testes particular. Um programa com cobertura de código alta foi mais exaustivamente testado e tem uma menor chance de conter erros de software do que um programa com baixa cobertura de código.

 
 --Wikipedia

Neste capítulo você aprenderá tudo sobre a funcionalidade de cobertura de código do PHPUnit que lhe dará uma perspicácia sobre quais partes do código de produção são executadas quando os testes são executados. Ele faz uso do componente PHP_CodeCoverage, que por sua vez utiliza a funcionalidade de cobertura de código fornecido pela extensão Xdebug para PHP.

Nota

Xdebug não é distribuído como parte do PHPUnit. Se você receber um aviso durante a execução de testes que a extensão Xdebug não está carregada, isso significa que Xdebug ou não está instalada ou não está configurada corretamente. Antes que você possa usar as funcionalidades de análise de cobertura de código no PHPUnit, você deve ler o manual de instalação Xdebug.

PHPUnit pode gerar um relatório de cobertura de código baseado em HTML, tal como arquivos de registros baseado em XML com informações de cobertura de código em vários formatos (Clover, Crap4J, PHPUnit). Informação de cobertura de código também pode ser relatado como texto (e impresso na STDOUT) e exportado como código PHP para futuro processamento.

Por favor, consulte o Capítulo 3 para uma lista de opções de linha de comando que controlam a funcionalidade de cobertura de código, bem como em “Registrando” para as definições de configurações relevantes.

Métricas de Software para Cobertura de Código

Várias métricas de software existem para mensurar a cobertura de código:

Cobertura de Linha

A métrica de software Cobertura de Linha mensura se cada linha executável foi executada.

Cobertura de Função e Método

A métrica de software Cobertura de Função e Método mensura se cada função ou método foi invocado. PHP_CodeCoverage só considera uma função ou método como coberto quando todas suas linhas executáveis são cobertas.

Cobertura de Classe e Trait

A métrica de software Cobertura de Classe e Trait mensura se cada método de uma classe ou trait é coberto. PHP_CodeCoverage só considera uma classe ou trait como coberta quando todos seus métodos são cobertos.

Cobertura de Código de Operação

A métrica de software Cobertura de Código de Operação mensura se cada código de operação de uma função ou método foi executado enquanto executa a suíte de teste. Uma linha de código geralmente compila em mais de um código de operação. Cobertura de Linha considera uma linha de código como coberta logo que um dos seus códigos de operações é executado.

Cobertura de Ramo

A métrica de software Cobertura de Ramo mensura se cada expressão booleana de cada estrutura de controle avaliada a true e false enquanto executa a suíte de teste.

Cobertura de Caminho

A métrica de software Cobertura de Caminho mensura se cada um dos caminhos de execução possíveis em uma função ou método foi seguido durante a execução da suíte de teste. Um caminho de execução é uma sequência única de ramos a partir da entrada de uma função ou método para a sua saída.

Índice de Anti-Patterns de Mudança de Risco (CRAP - Change Risk Anti-Patterns)

O Índice de Anti-Patterns de Mudança de Risco (CRAP - Change Risk Anti-Patterns) é calculado baseado na complexidade ciclomática e cobertura de código de uma unidade de código. Código que não é muito complexo e tem uma cobertura de teste adequada terá um baixo índice CRAP. O índice CRAP pode ser reduzido escrevendo testes e refatorando o código para reduzir sua complexidade.

Nota

As métricas de software Cobertura de Código de Operação, Cobertura de Ramo e Cobertura de Caminho ainda não são suportadas pelo PHP_CodeCoverage.

Incluindo e Excluindo Arquivos

Por padrão, todos os arquivos de código-fonte que contém pelo menos uma linha de código que tenha sido executada (e apenas esses arquivos) são incluídos no relatório de cobertura de código.

Por padrão, uma lista-negra é usada para excluir arquivos do relatório de cobertura de código. Essa lista-negra é previamente preenchida com os arquivos-fonte do PHPUnit e suas dependências.

É uma boa prática usar um lista-branca ao invés da lista-negra mencionada acima.

Opcionalmente, todos arquivos da lista-branca pode ser adicionados ao relatório de cobertura de código pela definição addUncoveredFilesFromWhitelist="true" em suas configurações do PHPUnit (veja “Incluindo e Excluindo Arquivos para Cobertura de Código”). Isso permite a inclusão de arquivos que ainda não são testados em tudo. Se você quer obter informação sobre quais linhas de um tal arquivo descoberto são executadas, por exemplo, você também precisa definir processUncoveredFilesFromWhitelist="true" em suas configurações do PHPUnit (veja “Incluindo e Excluindo Arquivos para Cobertura de Código”).

Nota

Por favor, note que o carregamento de arquivos de código-fonte que é realizado, quando processUncoveredFilesFromWhitelist="true" é definido, pode causar problemas quando um arquivo de código-fonte contém código fora do escopo de uma classe ou função, por exemplo.

Ignorando Blocos de Código

Às vezes você tem blocos de código que não pode testar e que pode querer ignorar durante a análise de cobertura de código. O PHPUnit permite que você o faça usando as anotações @codeCoverageIgnore, @codeCoverageIgnoreStart e @codeCoverageIgnoreEnd como mostrado em Exemplo 11.1.

Exemplo 11.1: Usando as anotações @codeCoverageIgnore, @codeCoverageIgnoreStart e @codeCoverageIgnoreEnd

<?php
/**
 * @codeCoverageIgnore
 */
class Foo
{
    public function bar()
    {
    }
}

class Bar
{
    /**
     * @codeCoverageIgnore
     */
    public function foo()
    {
    }
}

if (FALSE) {
    // @codeCoverageIgnoreStart
    print '*';
    // @codeCoverageIgnoreEnd
}

exit; // @codeCoverageIgnore
?>

As linhas de código ignoradas (marcadas como ignoradas usando as anotações) são contadas como executadas (se forem executáveis) e não serão destacadas.

Especificando métodos cobertos

A anotação @covers (veja Tabela B.1) pode ser usada em um código de teste para especificar qual(is) método(s) um método de teste quer testar. Se fornecido, apenas a informação de cobertura de código para o(s) método(s) especificado(s) será considerada. Exemplo 11.2 mostra um exemplo.

Exemplo 11.2: Testes que especificam quais métodos querem cobrir

<?php
class BankAccountTest extends PHPUnit_Framework_TestCase
{
    protected $ba;

    protected function setUp()
    {
        $this->ba = new BankAccount;
    }

    /**
     * @covers BankAccount::getBalance
     */
    public function testBalanceIsInitiallyZero()
    {
        $this->assertEquals(0, $this->ba->getBalance());
    }

    /**
     * @covers BankAccount::withdrawMoney
     */
    public function testBalanceCannotBecomeNegative()
    {
        try {
            $this->ba->withdrawMoney(1);
        }

        catch (BankAccountException $e) {
            $this->assertEquals(0, $this->ba->getBalance());

            return;
        }

        $this->fail();
    }

    /**
     * @covers BankAccount::depositMoney
     */
    public function testBalanceCannotBecomeNegative2()
    {
        try {
            $this->ba->depositMoney(-1);
        }

        catch (BankAccountException $e) {
            $this->assertEquals(0, $this->ba->getBalance());

            return;
        }

        $this->fail();
    }

    /**
     * @covers BankAccount::getBalance
     * @covers BankAccount::depositMoney
     * @covers BankAccount::withdrawMoney
     */
    public function testDepositWithdrawMoney()
    {
        $this->assertEquals(0, $this->ba->getBalance());
        $this->ba->depositMoney(1);
        $this->assertEquals(1, $this->ba->getBalance());
        $this->ba->withdrawMoney(1);
        $this->assertEquals(0, $this->ba->getBalance());
    }
}
?>

Também é possível especificar que um teste não deve cobrir qualquer método usando a anotação @coversNothing (veja “@coversNothing”). Isso pode ser útil quando escrever testes de integração para certificar-se de que você só gerará cobertura de código com testes unitários.

Exemplo 11.3: Um teste que especifica que nenhum método deve ser coberto

<?php
class GuestbookIntegrationTest extends PHPUnit_Extensions_Database_TestCase
{
    /**
     * @coversNothing
     */
    public function testAddEntry()
    {
        $guestbook = new Guestbook();
        $guestbook->addEntry("suzy", "Hello world!");

        $queryTable = $this->getConnection()->createQueryTable(
            'guestbook', 'SELECT * FROM guestbook'
        );

        $expectedTable = $this->createFlatXmlDataSet("expectedBook.xml")
                              ->getTable("guestbook");

        $this->assertTablesEqual($expectedTable, $queryTable);
    }
}
?>
      

Casos Extremos

Esta seção mostra casos extremos notáveis que induz a confundir a informação de cobertura de código.

Exemplo 11.4:

<?php
// Por ser "baseado em linha" e não em declaração,
// uma linha sempre terá um estado de cobertura
if (false) this_function_call_shows_up_as_covered();

// Devido ao modo que a cobertura de código funciona internamente, estas duas linhas são especiais.
// Esta linha vai aparecer como não-executável
if (false)
    // Esta linha vai aparecer como coberta, pois de fato é a cobertura
    // da declaração if da linha anterior que é mostrada aqui!
    will_also_show_up_as_covered();

// Para evitar isso é necessário usar chaves
if (false) {
    this_call_will_never_show_up_as_covered();
}
?>

Capítulo 12. Outros Usos para Testes

Uma vez que você se acostumar a escrever testes automatizados, você vai querer descobrir mais usos para testes. Aqui temos alguns exemplos.

Documentação Ágil

Tipicamente, em um projeto desenvolvido usando um processo ágil, como a Programação Extrema, a documentação não pode se manter com as mudanças frequentes do design e código do projeto. Programação Extrema exige propriedade coletiva de código, então todos os desenvolvedores precisam saber como o sistema todo funciona. Se você for disciplinado o suficiente para consequentemente usar "nomes falantes" para seus testes que descrevam o que cada classe deveria fazer, você pode usar a funcionalidade TestDox do PHPUnit para gerar documentação automatizada para seu projeto baseada nos testes. Essa documentação dá aos desenvolvedores uma visão geral sobre o que cada classe do projeto deveria fazer.

A funcionalidade TestDox do PHPUnit olha para uma classe de teste e todos os nomes dos métodos de teste e os converte de nomes camelCase do PHP para sentenças: testBalanceIsInitiallyZero() se torna "Balance is initially zero". Se houverem vários métodos de teste cujos nomes apenas diferem em um sufixo de um ou mais dígitos, como em testBalanceCannotBecomeNegative() e testBalanceCannotBecomeNegative2(), a sentença "Balance is initially zero" aparecerá apenas uma vez, assumindo-se que todos os testes foram bem-sucedidos.

Vamos dar uma olhada na documentação ágil gerada para a classe BankAccount:

phpunit --testdox BankAccountTest
PHPUnit 5.7.0 by Sebastian Bergmann and contributors.

BankAccount
 [x] Balance is initially zero
 [x] Balance cannot become negative

Alternativamente, a documentação ágil pode ser gerada nos formatos HTML ou texto plano e escrita em um arquivo usando os argumentos --testdox-html e --testdox-text.

A Documentação Ágil pode ser usada para documentar as suposições que você faz sobre os pacotes externos que você usa em seu projeto. Quando você usa um pacote externo, você está exposto ao risco do pacote não se comportar como você espera, e de futuras versões do pacote mudarem de formas súbitas que quebrarão o seu código, sem que você saiba. Você pode dar um jeito nesses problemas escrevendo um teste toda vez que fizer uma suposição. Se seu teste for bem sucedido, sua suposição é válida. Se você documentar todas as suas suposições com testes, futuras versões do pacote externo não serão motivo de preocupação: se o teste for bem-sucedido, seu sistema deverá continuar funcionando.

Testes Inter-Equipes

Quando você documenta suposições com testes, você possui os testes. O fornecedor do pacote -- sobre o qual você faz suposições -- não sabe nada sobre seus testes. Se você quer ter um relacionamento mais próximo com o fornecedor do pacote, você pode usar os testes para comunicar e coordenar suas atividades.

Quando você concorda em coordenar suas atividades com o fornecedor de um pacote, vocês podem escrever os testes juntos. Faça isso de modo que os testes revelem o máximo possível de suposições. Suposições escondidas são a morte da cooperação. Com os testes você documenta exatamente o que você espera de um pacote fornecido. O fornecedor vai saber que o pacote está completo quando todos os testes executarem.

Usando esboços (veja o capítulo em "Objetos Falsos", anteriormente neste livro), você pode chegar a se desassociar do fornecedor: O trabalho do fornecedor é fazer os testes executarem com a implementação real do pacote. O seu trabalho é fazer os testes executarem para seu próprio código. Até esse momento como você tem a implementação real do pacote fornecido, você usa objetos esboçados. Seguindo esta abordagem, os dois times podem desenvolver independentemente.

Capítulo 13. PHPUnit e Selenium

Servidor Selenium

Servidor Selenium é uma ferramenta de testes que permite a você escrever testes automatizados de interface de usuário para aplicações web em qualquer linguagem de programação contra qualquer website HTTP usando um dos principais navegadores. Ele realiza tarefas automatizadas no navegador guiando seu processo através do sistema operacional. O Selenium executa os testes diretamente em um navegador, exatamente como os usuários reais fazem. Esses testes podem ser usados para ambos testes de aceitação (realizando testes de alto-nível no sistema integrado em vez de apenas testar cada unidade do sistema independentemente) e testes de compatibilidade de navegador (testando a aplicação web em diferentes sistemas operacionais e navegadores).

O único cenário suportado do PHPUnit_Selenium é o do servidor Selenium 2.x. O servidor pode ser acessado através da Api clássica Selenium RC, já presente no 1.x, ou com a API WebDriver (parcialmente implementada) do PHPUnit_Selenium 1.2.

A razão por trás dessa decisão é que o Selenium 2 é compatível e o Selenium RC não é mais mantido.

Instalação

Primeiro, instale o Servidor Selenium:

  1. Baixe um arquivo de distribuição do Servidor Selenium.
  2. Descompacte o arquivo de distribuição e copie o selenium-server-standalone-2.9.0.jar (verifique o sufixo da versão) para /usr/local/bin, por exemplo.
  3. Inicie o Servidor Selenium executando java -jar /usr/local/bin/selenium-server-standalone-2.9.0.jar.

O pacote PHPUnit_Selenium está incluso na distribuição PHAR do PHPUnit. Ele pode ser instalado através do Composer, adicionando a seguinte dependência "require-dev":

"phpunit/phpunit-selenium": ">=1.2"

Agora podemos enviar comandos para o Servidor Selenium usando seu protocolo cliente/servidor.

PHPUnit_Extensions_Selenium2TestCase

O caso de teste PHPUnit_Extensions_Selenium2TestCase permite a você usar a API WebDriver (parcialmente implementada).

Exemplo 13.1 mostra como testar os conteúdos do elemento <title> do site http://www.example.com/.

Exemplo 13.1: Exemplo de uso para PHPUnit_Extensions_Selenium2TestCase

<?php
class WebTest extends PHPUnit_Extensions_Selenium2TestCase
{
    protected function setUp()
    {
        $this->setBrowser('firefox');
        $this->setBrowserUrl('http://www.example.com/');
    }

    public function testTitle()
    {
        $this->url('http://www.example.com/');
        $this->assertEquals('Example WWW Page', $this->title());
    }

}
?>
phpunit WebTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 28 seconds, Memory: 3.00Mb

There was 1 failure:

1) WebTest::testTitle
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'Example WWW Page'
+'IANA — Example domains'

/home/giorgio/WebTest.php:13

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Os comandos do Selenium2TestCase são implementados via __call(). Por favor, recorra ao teste de ponta a ponta para o PHPUnit_Extensions_Selenium2TestCase para uma lista de cada característica suportada.

PHPUnit_Extensions_SeleniumTestCase

A extensão de caso de teste PHPUnit_Extensions_SeleniumTestCase implementa o protocolo cliente/servidor para conversar com o Servidor Selenium assim como métodos de asserção especializados para testes web.

O Exemplo 13.2 mostra como testar os conteúdos do elemento <title> para o site http://www.exemplo.com/.

Exemplo 13.2: Exemplo de uso para PHPUnit_Extensions_SeleniumTestCase

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

class WebTest extends PHPUnit_Extensions_SeleniumTestCase
{
    protected function setUp()
    {
        $this->setBrowser('*firefox');
        $this->setBrowserUrl('http://www.example.com/');
    }

    public function testTitle()
    {
        $this->open('http://www.example.com/');
        $this->assertTitle('Example WWW Page');
    }
}
?>
phpunit WebTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 9 seconds, Memory: 6.00Mb

There was 1 failure:

1) WebTest::testTitle
Current URL: http://www.iana.org/domains/example/

Failed asserting that 'IANA — Example domains' matches PCRE pattern "/Example WWW Page/".


FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Diferente da classe PHPUnit_Framework_TestCase, classes de caso de teste que estendem o PHPUnit_Extensions_SeleniumTestCase têm que fornecer um método setUp(). Esse método é usado para configurar a sessão do Servidor Selenium. Veja Tabela 13.1 para uma lista de métodos que estão disponíveis para isso.

Tabela 13.1. API do Servidor Selenium: Setup

MétodoSignificado
void setBrowser(string $browser)Define o navegador a ser usado pelo Servidor Selenium.
void setBrowserUrl(string $browserUrl)Define a URL base para os testes.
void setHost(string $host)Define o nome do host para a conexão do Servidor Selenium.
void setPort(int $port)Define a porta de conexão para o Servidor Selenium
void setTimeout(int $timeout)Define o tempo de espera para a conexão do Servidor Selenium.
void setSleep(int $seconds)Define o número de segundos que o cliente do Servidor Selenium deve esperar entre cada envio de comandos de ação para o Servidor Selenium.

O PHPUnit pode opcionalmente capturar a tela quando um teste do Selenium falha. Para habilitar isso, configure $captureScreenshotOnFailure, $screenshotPath e $screenshotUrl em sua classe de caso de teste como mostrado em Exemplo 13.3.

Exemplo 13.3: Capturando a tela quando um teste falha

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

class WebTest extends PHPUnit_Extensions_SeleniumTestCase
{
    protected $captureScreenshotOnFailure = TRUE;
    protected $screenshotPath = '/var/www/localhost/htdocs/screenshots';
    protected $screenshotUrl = 'http://localhost/screenshots';

    protected function setUp()
    {
        $this->setBrowser('*firefox');
        $this->setBrowserUrl('http://www.example.com/');
    }

    public function testTitle()
    {
        $this->open('http://www.example.com/');
        $this->assertTitle('Example WWW Page');
    }
}
?>
phpunit WebTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 7 seconds, Memory: 6.00Mb

There was 1 failure:

1) WebTest::testTitle
Current URL: http://www.iana.org/domains/example/
Screenshot: http://localhost/screenshots/334b080f2364b5.21568ee1c7f6742c9.png

Failed asserting that 'IANA — Example domains' matches PCRE pattern "/Example WWW Page/".


FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Você pode executar cada teste usando um conjunto de navegadores: Em vez de usar setBrowser() para configurar um navegador, você pode declarar um vetor public static chamado $browsers em sua classe de caso de testes. Cada item nesse vetor descreve uma configuração de navegador. Cada um desses navegadores pode ser hospedado em diferentes Servidores Selenium. O Exemplo 13.4 mostra um exemplo.

Exemplo 13.4: Definindo configurações de múltiplos navegadores

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

class WebTest extends PHPUnit_Extensions_SeleniumTestCase
{
    public static $browsers = array(
      array(
        'name'    => 'Firefox on Linux',
        'browser' => '*firefox',
        'host'    => 'my.linux.box',
        'port'    => 4444,
        'timeout' => 30000,
      ),
      array(
        'name'    => 'Safari on MacOS X',
        'browser' => '*safari',
        'host'    => 'my.macosx.box',
        'port'    => 4444,
        'timeout' => 30000,
      ),
      array(
        'name'    => 'Safari on Windows XP',
        'browser' => '*custom C:\Program Files\Safari\Safari.exe -url',
        'host'    => 'my.windowsxp.box',
        'port'    => 4444,
        'timeout' => 30000,
      ),
      array(
        'name'    => 'Internet Explorer on Windows XP',
        'browser' => '*iexplore',
        'host'    => 'my.windowsxp.box',
        'port'    => 4444,
        'timeout' => 30000,
      )
    );

    protected function setUp()
    {
        $this->setBrowserUrl('http://www.example.com/');
    }

    public function testTitle()
    {
        $this->open('http://www.example.com/');
        $this->assertTitle('Example Web Page');
    }
}
?>

PHPUnit_Extensions_SeleniumTestCase pode coletar a informação de cobertura de código para execução de testes através do Selenium:

  1. Copie PHPUnit/Extensions/SeleniumCommon/phpunit_coverage.php dentro do seu diretório raiz de documentos do servidor web.
  2. Em seu arquivo de configuração php.ini do servidor web, configure PHPUnit/Extensions/SeleniumCommon/prepend.php e PHPUnit/Extensions/SeleniumCommon/append.php como o auto_prepend_file e auto_append_file, respectivamente.
  3. Na sua classe de caso de teste que estende PHPUnit_Extensions_SeleniumTestCase, use
    protected $coverageScriptUrl = 'http://host/phpunit_coverage.php';
    para configurar a URL para o script phpunit_coverage.php.

Tabela 13.2 lista os vários métodos de asserção que o PHPUnit_Extensions_SeleniumTestCase fornece.

Tabela 13.2. Asserções

AsserçãoSignificado
void assertElementValueEquals(string $locator, string $text)Relata um erro se o valor do elemento identificado por $locator não é igual ao $text informado.
void assertElementValueNotEquals(string $locator, string $text)Relata um erro se o valor do elemento identificado por $locator é igual ao $text informado.
void assertElementValueContains(string $locator, string $text)Relata um erro se o valor do elemento identificado por $locator não contém o $text informado.
void assertElementValueNotContains(string $locator, string $text)Relata um erro se o valor do elemento identificado por $locator contém o $text informado.
void assertElementContainsText(string $locator, string $text)Relata um erro se o elemento identificado por $locator não contém o $text informado.
void assertElementNotContainsText(string $locator, string $text)Relata um erro se o elemento identificado por $locator contém o $text informado.
void assertSelectHasOption(string $selectLocator, string $option)Relata um erro se a opção informada não estiver disponível.
void assertSelectNotHasOption(string $selectLocator, string $option)Relata um erro se a opção informada estiver disponível.
void assertSelected($selectLocator, $option)Relata um erro se o rótulo informado não estiver selecionado.
void assertNotSelected($selectLocator, $option)Relata um erro se o rótulo informado estiver selecionado.
void assertIsSelected(string $selectLocator, string $value)Relata um erro se o valor informado não estiver selecionado.
void assertIsNotSelected(string $selectLocator, string $value)Relata um erro se o valor informado estiver selecionado.

Tabela 13.3 mostra o método modelo de PHPUnit_Extensions_SeleniumTestCase:

Tabela 13.3. Métodos Modelo

MétodoSignificado
void defaultAssertions()Sobreposição para realizar asserções que são compartilhadas por todos os testes de um caso de teste. Este método é chamado após cada comando ser enviado ao Servidor Selenium.

Por favor, verifique a documentação dos comandos do Selenium para uma referência de todos os comandos disponíveis e como eles são utilizados.

Os comandos do Selenium 1 são implementados dinamicamente via __call. Verifique também a documentação da API para PHPUnit_Extensions_SeleniumTestCase_Driver::__call() para uma lista de todos os métodos suportados do lado do PHP, juntamente com argumentos e tipos de retorno quando disponíveis.

Usando o método runSelenese($filename) você também pode executar um teste Selenium a partir de sua especificação Selenese/HTML. Além disso, usando o atributo estático $seleneseDirectory, você pode criar automaticamente objetos de teste a partir de um diretório que contenha arquivos Selenese/HTML. O diretório especificado é pesquisado recursivamente por arquivos .htm que possam conter Selenese/HTML. O Exemplo 13.5 mostra um exemplo.

Exemplo 13.5: Usando um diretório de arquivos Selenese/HTML como testes

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

class SeleneseTests extends PHPUnit_Extensions_SeleniumTestCase
{
    public static $seleneseDirectory = '/path/to/files';
}
?>

Desde o Selenium 1.1.1, um recurso experimental foi incluído para permitir ao usuário compartilhar a sessão entre testes. O único caso suportado é compartilhar a sessão entre todos os testes quando um único navegador é usado. Chame PHPUnit_Extensions_SeleniumTestCase::shareSession(true) em seu arquivo bootstrap para habilitar o compartilhamento de sessão. A sessão será reiniciada no caso de testes mal-sucedidos (falhos ou incompletos); cabe ao usuário evitar interações entre testes seja resetando cookies ou deslogando da aplicação sob teste (com um método tearDown()).

Capítulo 14. Registrando

O PHPUnit pode produzir vários tipos de arquivos de registro (logfiles).

Resultados de Teste (XML)

O arquivo de registro XML para resultados de testes produzidos pelo PHPUnit é baseado naquele usado pela tarefa do JUnit para Apache Ant. O seguinte exemplo mostra o arquivo de registro XML gerado para os testes em ArrayTest:

<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
  <testsuite name="ArrayTest"
             file="/home/sb/ArrayTest.php"
             tests="2"
             assertions="2"
             failures="0"
             errors="0"
             time="0.016030">
    <testcase name="testNewArrayIsEmpty"
              class="ArrayTest"
              file="/home/sb/ArrayTest.php"
              line="6"
              assertions="1"
              time="0.008044"/>
    <testcase name="testArrayContainsAnElement"
              class="ArrayTest"
              file="/home/sb/ArrayTest.php"
              line="15"
              assertions="1"
              time="0.007986"/>
  </testsuite>
</testsuites>

O seguinte arquivo de registro XML foi gerado por dois testes, testFailure e testError, de uma classe de caso de teste chamada FailureErrorTest e mostra como falhas e erros são denotadas.

<?xml version="1.0" encoding="UTF-8"?>
<testsuites>
  <testsuite name="FailureErrorTest"
             file="/home/sb/FailureErrorTest.php"
             tests="2"
             assertions="1"
             failures="1"
             errors="1"
             time="0.019744">
    <testcase name="testFailure"
              class="FailureErrorTest"
              file="/home/sb/FailureErrorTest.php"
              line="6"
              assertions="1"
              time="0.011456">
      <failure type="PHPUnit_Framework_ExpectationFailedException">
testFailure(FailureErrorTest)
Failed asserting that &lt;integer:2&gt; matches expected value &lt;integer:1&gt;.

/home/sb/FailureErrorTest.php:8
</failure>
    </testcase>
    <testcase name="testError"
              class="FailureErrorTest"
              file="/home/sb/FailureErrorTest.php"
              line="11"
              assertions="0"
              time="0.008288">
      <error type="Exception">testError(FailureErrorTest)
Exception:

/home/sb/FailureErrorTest.php:13
</error>
    </testcase>
  </testsuite>
</testsuites>

Resultados de Teste (TAP)

O Protocolo de Qualquer Teste (TAP - Test Anything Protocol) é uma interface simples baseada em texto do Perl entre módulos de teste. O seguinte exemplo mostra o arquivo de registro TAP gerado para os testes em ArrayTest:

TAP version 13
ok 1 - testNewArrayIsEmpty(ArrayTest)
ok 2 - testArrayContainsAnElement(ArrayTest)
1..2

O seguinte arquivo de registro TAP foi gerado para dois testes, testFailure e testError, de uma classe de caso de teste chamada FailureErrorTest e mostra como falhas e erros são denotados.

TAP version 13
not ok 1 - Failure: testFailure(FailureErrorTest)
  ---
  message: 'Failed asserting that <integer:2> matches expected value <integer:1>.'
  severity: fail
  data:
    got: 2
    expected: 1
  ...
not ok 2 - Error: testError(FailureErrorTest)
1..2

Resultados de Teste (JSON)

A Notação de Objeto JavaScript (JSON - JavaScript Object Notation) é um formato de intercâmbio de dados leve. O seguinte exemplo mostra as mensagens JSON geradas para os testes em ArrayTest:

{"event":"suiteStart","suite":"ArrayTest","tests":2}
{"event":"test","suite":"ArrayTest",
 "test":"testNewArrayIsEmpty(ArrayTest)","status":"pass",
 "time":0.000460147858,"trace":[],"message":""}
{"event":"test","suite":"ArrayTest",
 "test":"testArrayContainsAnElement(ArrayTest)","status":"pass",
 "time":0.000422954559,"trace":[],"message":""}

As mensagens JSON seguintes foram geradas para dois testes, testFailure e testError, de uma classe de caso de teste chamada FailureErrorTest e mostra como falhas e erros são denotados.

{"event":"suiteStart","suite":"FailureErrorTest","tests":2}
{"event":"test","suite":"FailureErrorTest",
 "test":"testFailure(FailureErrorTest)","status":"fail",
 "time":0.0082459449768066,"trace":[],
 "message":"Failed asserting that <integer:2> is equal to <integer:1>."}
{"event":"test","suite":"FailureErrorTest",
 "test":"testError(FailureErrorTest)","status":"error",
 "time":0.0083680152893066,"trace":[],"message":""}

Cobertura de Código (XML)

O formato XML para registro de informação de cobertura de código produzido pelo PHPUnit é amplamente baseado naquele usado pelo Clover. O seguinte exemplo mostra o arquivo de registro XML gerado para os testes em BankAccountTest:

<?xml version="1.0" encoding="UTF-8"?>
<coverage generated="1184835473" phpunit="3.6.0">
  <project name="BankAccountTest" timestamp="1184835473">
    <file name="/home/sb/BankAccount.php">
      <class name="BankAccountException">
        <metrics methods="0" coveredmethods="0" statements="0"
                 coveredstatements="0" elements="0" coveredelements="0"/>
      </class>
      <class name="BankAccount">
        <metrics methods="4" coveredmethods="4" statements="13"
                 coveredstatements="5" elements="17" coveredelements="9"/>
      </class>
      <line num="77" type="method" count="3"/>
      <line num="79" type="stmt" count="3"/>
      <line num="89" type="method" count="2"/>
      <line num="91" type="stmt" count="2"/>
      <line num="92" type="stmt" count="0"/>
      <line num="93" type="stmt" count="0"/>
      <line num="94" type="stmt" count="2"/>
      <line num="96" type="stmt" count="0"/>
      <line num="105" type="method" count="1"/>
      <line num="107" type="stmt" count="1"/>
      <line num="109" type="stmt" count="0"/>
      <line num="119" type="method" count="1"/>
      <line num="121" type="stmt" count="1"/>
      <line num="123" type="stmt" count="0"/>
      <metrics loc="126" ncloc="37" classes="2" methods="4" coveredmethods="4"
               statements="13" coveredstatements="5" elements="17"
               coveredelements="9"/>
    </file>
    <metrics files="1" loc="126" ncloc="37" classes="2" methods="4"
             coveredmethods="4" statements="13" coveredstatements="5"
             elements="17" coveredelements="9"/>
  </project>
</coverage>

Cobertura de Código (TEXT)

Saída de cobertura de código legível para linha-de-comando ou arquivo de texto. O objetivo deste formato de saída é fornecer uma rápida visão geral de cobertura enquanto se trabalha em um pequeno grupo de classes. Para projetos maiores esta saída pode ser útil para conseguir uma rápida visão geral da cobertura do projeto ou quando usado com a funcionalidade --filter. Quando usada da linha-de-comando escrevendo php://stdout isso vai honrar a configuração --colors. Escrever na saída padrão é a opção padrão quando usado a partir da linha-de-comando. Por padrão isso só vai mostrar arquivos que tenham pelo menos uma linha coberta. Isso só pode ser alterado através da opção de configuração xml showUncoveredFiles. Veja “Registrando”. Por padrão todos arquivos e seus status de cobertura são mostrados no relatório detalhado. Isso pode ser alterado via através da opção de configuração xml showOnlySummary.

Capítulo 15. 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 no Exemplo 15.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 15.1: Os métodos assertTrue() e isTrue() da classe PHPUnitFramework_Assert

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

    /**
     * Assevera que uma condição é verdadeira (true).
     *
     * @param  boolean $condition
     * @param  string  $message
     * @throws PHPUnit_Framework_AssertionFailedError
     */
    public static function assertTrue($condition, $message = '')
    {
        self::assertThat($condition, self::isTrue(), $message);
    }

    // ...

    /**
     * Retorna um objeto comparador 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;
    }

    // ...
}?>

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

Exemplo 15.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 $other. Retorna TRUE se a
     * restrição é confirmada, FALSE caso contrário.
     *
     * @param mixed $other Valor ou objeto a avaliar.
     * @return bool
     */
    public function matches($other)
    {
        return $other === TRUE;
    }

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

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 falsificados.

Implementando PHPUnit_Framework_TestListener

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

Exemplo 15.3: Um simples ouvinte de teste

<?php
class SimpleTestListener implements PHPUnit_Framework_TestListener
{
    public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        printf("Error while running test '%s'.\n", $test->getName());
    }

    public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_AssertionFailedError $e, $time)
    {
        printf("Test '%s' failed.\n", $test->getName());
    }

    public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        printf("Test '%s' is incomplete.\n", $test->getName());
    }

    public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        printf("Test '%s' is deemed risky.\n", $test->getName());
    }

    public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
    {
        printf("Test '%s' has been skipped.\n", $test->getName());
    }

    public function startTest(PHPUnit_Framework_Test $test)
    {
        printf("Test '%s' started.\n", $test->getName());
    }

    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        printf("Test '%s' ended.\n", $test->getName());
    }

    public function startTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        printf("TestSuite '%s' started.\n", $suite->getName());
    }

    public function endTestSuite(PHPUnit_Framework_TestSuite $suite)
    {
        printf("TestSuite '%s' ended.\n", $suite->getName());
    }
}
?>

Exemplo 15.4 mostra como a subclasse da classe abstrata PHPUnit_Framework_BaseTestListener, que permite que você especifique apenas os métodos de interface que são interessantes para seu caso de uso, ao fornecer implementações vazias para todos os outros.

Exemplo 15.4: Usando o ouvinte de teste base

<?php
class ShortTestListener extends PHPUnit_Framework_BaseTestListener
{
    public function endTest(PHPUnit_Framework_Test $test, $time)
    {
        printf("Test '%s' ended.\n", $test->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

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

O PHPUnit navega com um decorador de teste concreto: PHPUnit_Extensions_RepeatedTest. Ele é usado para executar um um teste repetidamente e apenas o conta como bem-sucedido se todas as iterações forem bem-sucedidas.

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

Exemplo 15.5: O Decorador RepeatedTest

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

class PHPUnit_Extensions_RepeatedTest extends PHPUnit_Extensions_TestDecorator
{
    private $timesRepeat = 1;

    public function __construct(PHPUnit_Framework_Test $test, $timesRepeat = 1)
    {
        parent::__construct($test);

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

    public function count()
    {
        return $this->timesRepeat * $this->test->count();
    }

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

        for ($i = 0; $i < $this->timesRepeat && !$result->shouldStop(); $i++) {
            $this->test->run($result);
        }

        return $result;
    }
}
?>

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 testes guiados por dados, por exemplo.

O Exemplo 15.6 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 valor é o real.

Exemplo 15.6: Um teste guiado por dados

<?php
class DataDrivenTest implements PHPUnit_Framework_Test
{
    private $lines;

    public function __construct($dataFile)
    {
        $this->lines = file($dataFile);
    }

    public function count()
    {
        return 1;
    }

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

        foreach ($this->lines as $line) {
            $result->startTest($this);
            PHP_Timer::start();
            $stopTime = NULL;

            list($expected, $actual) = explode(';', $line);

            try {
                PHPUnit_Framework_Assert::assertEquals(
                  trim($expected), trim($actual)
                );
            }

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

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

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

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

        return $result;
    }
}

$test = new DataDrivenTest('data_file.csv');
$result = PHPUnit_TextUI_TestRunner::run($test);
?>
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

.F

Time: 0 seconds

There was 1 failure:

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

FAILURES!
Tests: 2, Failures: 1.

Apêndice A. Asserções

Esse apêndice lista os vários métodos de asserções que estão disponíveis.

assertArrayHasKey()

assertArrayHasKey(mixed $key, array $array[, string $message = ''])

Reporta um erro identificado pela $message se o $array não contém a $key.

assertArrayNotHasKey() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.1: Utilização de assertArrayHasKey()

<?php
class ArrayHasKeyTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertArrayHasKey('foo', array('bar' => 'baz'));
    }
}
?>
phpunit ArrayHasKeyTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) ArrayHasKeyTest::testFailure
Failed asserting that an array has the key 'foo'.

/home/sb/ArrayHasKeyTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertClassHasAttribute()

assertClassHasAttribute(string $attributeName, string $className[, string $message = ''])

Reporta um erro identificado pela $message se o $className::attributeName não existir.

assertClassNotHasAttribute() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.2: Utilização de assertClassHasAttribute()

<?php
class ClassHasAttributeTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertClassHasAttribute('foo', 'stdClass');
    }
}
?>
phpunit ClassHasAttributeTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 4.75Mb

There was 1 failure:

1) ClassHasAttributeTest::testFailure
Failed asserting that class "stdClass" has attribute "foo".

/home/sb/ClassHasAttributeTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertArraySubset()

assertArraySubset(array $subset, array $array[, bool $strict = '', string $message = ''])

Reporta um erro identificado pela $message se $array não contém o $subset.

$strict é uma flag usada para comparar a identidade de objetos dentro de arrays.

Exemplo A.3: Usage of assertArraySubset()

<?php
class ArraySubsetTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertArraySubset(['config' => ['key-a', 'key-b']], ['config' => ['key-a']]);
    }
}
?>
phpunit ArrayHasKeyTest
PHPUnit 4.4.0 by Sebastian Bergmann.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) Epilog\EpilogTest::testNoFollowOption
Failed asserting that an array has the subset Array &0 (
    'config' => Array &1 (
        0 => 'key-a'
        1 => 'key-b'
    )
).

/home/sb/ArraySubsetTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertClassHasStaticAttribute()

assertClassHasStaticAttribute(string $attributeName, string $className[, string $message = ''])

Reporta um erro identificado pela $message se o $className::attributeName não existir.

assertClassNotHasStaticAttribute() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.4: Utilização de assertClassHasStaticAttribute()

<?php
class ClassHasStaticAttributeTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertClassHasStaticAttribute('foo', 'stdClass');
    }
}
?>
phpunit ClassHasStaticAttributeTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 4.75Mb

There was 1 failure:

1) ClassHasStaticAttributeTest::testFailure
Failed asserting that class "stdClass" has static attribute "foo".

/home/sb/ClassHasStaticAttributeTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertContains()

assertContains(mixed $needle, Iterator|array $haystack[, string $message = ''])

Reporta um erro identificado pela $message se o $needle não é um elemento de $haystack.

assertNotContains() é o inverso desta asserção e recebe os mesmos argumentos.

assertAttributeContains() e assertAttributeNotContains() são invólucros convenientes que usam um atributo public, protected, ou private de uma classe ou objeto como a haystack.

Exemplo A.5: Utilização de assertContains()

<?php
class ContainsTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertContains(4, array(1, 2, 3));
    }
}
?>
phpunit ContainsTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) ContainsTest::testFailure
Failed asserting that an array contains 4.

/home/sb/ContainsTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertContains(string $needle, string $haystack[, string $message = '', boolean $ignoreCase = FALSE])

Reporta um erro identificado pela $message se o $needle não é uma substring de $haystack.

Se $ignoreCase é TRUE, o teste irá ser case insensitive.

Exemplo A.6: Utilização de assertContains()

<?php
class ContainsTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertContains('baz', 'foobar');
    }
}
?>
phpunit ContainsTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) ContainsTest::testFailure
Failed asserting that 'foobar' contains "baz".

/home/sb/ContainsTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Exemplo A.7: Utilização de assertContains() com $ignoreCase

<?php
class ContainsTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertContains('foo', 'FooBar');
    }

    public function testOK()
    {
        $this->assertContains('foo', 'FooBar', '', true);
    }
}
?>
phpunit ContainsTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F.

Time: 0 seconds, Memory: 2.75Mb

There was 1 failure:

1) ContainsTest::testFailure
Failed asserting that 'FooBar' contains "foo".

/home/sb/ContainsTest.php:6

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.

assertContainsOnly()

assertContainsOnly(string $type, Iterator|array $haystack[, boolean $isNativeType = NULL, string $message = ''])

Reporta um erro identificado pela $message se o $haystack não contém somente variáveis do tipo $type.

$isNativeType é uma flag usada para indicar se $type é um tipo nativo do PHP ou não.

assertNotContainsOnly() é o inverso desta asserção e recebe os mesmos argumentos.

assertAttributeContainsOnly() e assertAttributeNotContainsOnly() são invólucros convenientes que usam um atributo public, protected, ou private de uma classe ou objeto como a haystack.

Exemplo A.8: Utilização de assertContainsOnly()

<?php
class ContainsOnlyTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertContainsOnly('string', array('1', '2', 3));
    }
}
?>
phpunit ContainsOnlyTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) ContainsOnlyTest::testFailure
Failed asserting that Array (
    0 => '1'
    1 => '2'
    2 => 3
) contains only values of type "string".

/home/sb/ContainsOnlyTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertContainsOnlyInstancesOf()

assertContainsOnlyInstancesOf(string $classname, Traversable|array $haystack[, string $message = ''])

Reporta um erro identificado pela $message se o $haystack não contém somente instâncias da classe $classname.

Exemplo A.9: Utilização de assertContainsOnlyInstancesOf()

<?php
class ContainsOnlyInstancesOfTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertContainsOnlyInstancesOf('Foo', array(new Foo(), new Bar(), new Foo()));
    }
}
?>
phpunit ContainsOnlyInstancesOfTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) ContainsOnlyInstancesOfTest::testFailure
Failed asserting that Array ([0]=> Bar Object(...)) is an instance of class "Foo".

/home/sb/ContainsOnlyInstancesOfTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertCount()

assertCount($expectedCount, $haystack[, string $message = ''])

Reporta um erro identificado pela $message se o número de elementos no $haystack não for o $expectedCount.

assertNotCount() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.10: Utilização de assertCount()

<?php
class CountTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertCount(0, array('foo'));
    }
}
?>
phpunit CountTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 4.75Mb

There was 1 failure:

1) CountTest::testFailure
Failed asserting that actual size 1 matches expected size 0.

/home/sb/CountTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertEmpty()

assertEmpty(mixed $actual[, string $message = ''])

Reporta um erro identificado pela $message se o $actual não está vazio.

assertNotEmpty() é o inverso desta asserção e recebe os mesmos argumentos.

assertAttributeEmpty() e assertAttributeNotEmpty() são invólucros convenientes que podem ser aplicados para um atributo public, protected, or private de uma classe ou objeto.

Exemplo A.11: Utilização de assertEmpty()

<?php
class EmptyTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertEmpty(array('foo'));
    }
}
?>
phpunit EmptyTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 4.75Mb

There was 1 failure:

1) EmptyTest::testFailure
Failed asserting that an array is empty.

/home/sb/EmptyTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertEqualXMLStructure()

assertEqualXMLStructure(DOMElement $expectedElement, DOMElement $actualElement[, boolean $checkAttributes = FALSE, string $message = ''])

Reporta um erro identificado pela $message se a estrutura XML do DOMElement no $actualElement não é igual a estrutura XML do DOMElement no $expectedElement.

Exemplo A.12: Utilização de assertEqualXMLStructure()

<?php
class EqualXMLStructureTest extends PHPUnit_Framework_TestCase
{
    public function testFailureWithDifferentNodeNames()
    {
        $expected = new DOMElement('foo');
        $actual = new DOMElement('bar');

        $this->assertEqualXMLStructure($expected, $actual);
    }

    public function testFailureWithDifferentNodeAttributes()
    {
        $expected = new DOMDocument;
        $expected->loadXML('<foo bar="true" />');

        $actual = new DOMDocument;
        $actual->loadXML('<foo/>');

        $this->assertEqualXMLStructure(
          $expected->firstChild, $actual->firstChild, TRUE
        );
    }

    public function testFailureWithDifferentChildrenCount()
    {
        $expected = new DOMDocument;
        $expected->loadXML('<foo><bar/><bar/><bar/></foo>');

        $actual = new DOMDocument;
        $actual->loadXML('<foo><bar/></foo>');

        $this->assertEqualXMLStructure(
          $expected->firstChild, $actual->firstChild
        );
    }

    public function testFailureWithDifferentChildren()
    {
        $expected = new DOMDocument;
        $expected->loadXML('<foo><bar/><bar/><bar/></foo>');

        $actual = new DOMDocument;
        $actual->loadXML('<foo><baz/><baz/><baz/></foo>');

        $this->assertEqualXMLStructure(
          $expected->firstChild, $actual->firstChild
        );
    }
}
?>
phpunit EqualXMLStructureTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

FFFF

Time: 0 seconds, Memory: 5.75Mb

There were 4 failures:

1) EqualXMLStructureTest::testFailureWithDifferentNodeNames
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'foo'
+'bar'

/home/sb/EqualXMLStructureTest.php:9

2) EqualXMLStructureTest::testFailureWithDifferentNodeAttributes
Number of attributes on node "foo" does not match
Failed asserting that 0 matches expected 1.

/home/sb/EqualXMLStructureTest.php:22

3) EqualXMLStructureTest::testFailureWithDifferentChildrenCount
Number of child nodes of "foo" differs
Failed asserting that 1 matches expected 3.

/home/sb/EqualXMLStructureTest.php:35

4) EqualXMLStructureTest::testFailureWithDifferentChildren
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'bar'
+'baz'

/home/sb/EqualXMLStructureTest.php:48

FAILURES!
Tests: 4, Assertions: 8, Failures: 4.

assertEquals()

assertEquals(mixed $expected, mixed $actual[, string $message = ''])

Reporta um erro identificado pela $message se as variáveis $expected e $actual são iguais.

assertNotEquals() é o inverso desta asserção e recebe os mesmos argumentos.

assertAttributeEquals() e assertAttributeNotEquals() são invólucros convenientes que usam um atributo public, protected, or private de uma classe ou objeto como o valor atual.

Exemplo A.13: Utilização de assertEquals()

<?php
class EqualsTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertEquals(1, 0);
    }

    public function testFailure2()
    {
        $this->assertEquals('bar', 'baz');
    }

    public function testFailure3()
    {
        $this->assertEquals("foo\nbar\nbaz\n", "foo\nbah\nbaz\n");
    }
}
?>
phpunit EqualsTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

FFF

Time: 0 seconds, Memory: 5.25Mb

There were 3 failures:

1) EqualsTest::testFailure
Failed asserting that 0 matches expected 1.

/home/sb/EqualsTest.php:6

2) EqualsTest::testFailure2
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'bar'
+'baz'

/home/sb/EqualsTest.php:11

3) EqualsTest::testFailure3
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
 'foo
-bar
+bah
 baz
 '

/home/sb/EqualsTest.php:16

FAILURES!
Tests: 3, Assertions: 3, Failures: 3.

Comparações mais especializadas são usadas para especificar tipos de argumentos para $expected e $actual, veja abaixo.

assertEquals(float $expected, float $actual[, string $message = '', float $delta = 0])

Reporta um erro identificado pela $message se os dois floats $expected e $actual não estão dentro de $delta um do outro.

Por favor, leia "O que cada cientista de computador deve saber sobre Aritmética de Ponto Flutuante" para entender por que $delta é necessário.

Exemplo A.14: Utilização de assertEquals() with floats

<?php
class EqualsTest extends PHPUnit_Framework_TestCase
{
    public function testSuccess()
    {
        $this->assertEquals(1.0, 1.1, '', 0.2);
    }

    public function testFailure()
    {
        $this->assertEquals(1.0, 1.1);
    }
}
?>
phpunit EqualsTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

.F

Time: 0 seconds, Memory: 5.75Mb

There was 1 failure:

1) EqualsTest::testFailure
Failed asserting that 1.1 matches expected 1.0.

/home/sb/EqualsTest.php:11

FAILURES!
Tests: 2, Assertions: 2, Failures: 1.

assertEquals(DOMDocument $expected, DOMDocument $actual[, string $message = ''])

Reporta um erro identificado pela $message se a forma canonical não-comentada dos documentos XML representada pelos objetos DOMDocument $expected e $actual não são iguais.

Exemplo A.15: Utilização de assertEquals() com objetos DOMDocument

<?php
class EqualsTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $expected = new DOMDocument;
        $expected->loadXML('<foo><bar/></foo>');

        $actual = new DOMDocument;
        $actual->loadXML('<bar><foo/></bar>');

        $this->assertEquals($expected, $actual);
    }
}
?>
phpunit EqualsTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) EqualsTest::testFailure
Failed asserting that two DOM documents are equal.
--- Expected
+++ Actual
@@ @@
 <?xml version="1.0"?>
-<foo>
-  <bar/>
-</foo>
+<bar>
+  <foo/>
+</bar>

/home/sb/EqualsTest.php:12

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertEquals(object $expected, object $actual[, string $message = ''])

Reporta um erro identificado pela $message se os objetos $expected e $actual não tem valores de atributos iguais.

Exemplo A.16: Utilização de assertEquals() com objetos

<?php
class EqualsTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $expected = new stdClass;
        $expected->foo = 'foo';
        $expected->bar = 'bar';

        $actual = new stdClass;
        $actual->foo = 'bar';
        $actual->baz = 'bar';

        $this->assertEquals($expected, $actual);
    }
}
?>
phpunit EqualsTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) EqualsTest::testFailure
Failed asserting that two objects are equal.
--- Expected
+++ Actual
@@ @@
 stdClass Object (
-    'foo' => 'foo'
-    'bar' => 'bar'
+    'foo' => 'bar'
+    'baz' => 'bar'
 )

/home/sb/EqualsTest.php:14

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertEquals(array $expected, array $actual[, string $message = ''])

Reporta um erro identificado pela $message se os arrays $expected e $actual não são iguais.

Exemplo A.17: Utilização de assertEquals() com arrays

<?php
class EqualsTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertEquals(array('a', 'b', 'c'), array('a', 'c', 'd'));
    }
}
?>
phpunit EqualsTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) EqualsTest::testFailure
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
 Array (
     0 => 'a'
-    1 => 'b'
-    2 => 'c'
+    1 => 'c'
+    2 => 'd'
 )

/home/sb/EqualsTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertFalse()

assertFalse(bool $condition[, string $message = ''])

Reporta um erro identificado pela $message se o $condition é TRUE.

assertNotFalse() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.18: Utilização de assertFalse()

<?php
class FalseTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertFalse(TRUE);
    }
}
?>
phpunit FalseTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) FalseTest::testFailure
Failed asserting that true is false.

/home/sb/FalseTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertFileEquals()

assertFileEquals(string $expected, string $actual[, string $message = ''])

Reporta um erro identificado pela $message se o arquivo especificado pelo $expected não tem o mesmo conteúdo como o arquivo especificado pelo $actual.

assertFileNotEquals() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.19: Utilização de assertFileEquals()

<?php
class FileEqualsTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertFileEquals('/home/sb/expected', '/home/sb/actual');
    }
}
?>
phpunit FileEqualsTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) FileEqualsTest::testFailure
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'expected
+'actual
 '

/home/sb/FileEqualsTest.php:6

FAILURES!
Tests: 1, Assertions: 3, Failures: 1.

assertFileExists()

assertFileExists(string $filename[, string $message = ''])

Reporta um erro identificado pela $message se o arquivo especificado pelo $filename não existir.

assertFileNotExists() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.20: Utilização de assertFileExists()

<?php
class FileExistsTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertFileExists('/path/to/file');
    }
}
?>
phpunit FileExistsTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 4.75Mb

There was 1 failure:

1) FileExistsTest::testFailure
Failed asserting that file "/path/to/file" exists.

/home/sb/FileExistsTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertGreaterThan()

assertGreaterThan(mixed $expected, mixed $actual[, string $message = ''])

Reporta um erro identificado pela $message se o valor de $actual não é maior que o valor de $expected.

assertAttributeGreaterThan() é um invólucro conveniente que usa um atributo public, protected, ou private de uma classe ou objeto como o valor atual.

Exemplo A.21: Utilização de assertGreaterThan()

<?php
class GreaterThanTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertGreaterThan(2, 1);
    }
}
?>
phpunit GreaterThanTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) GreaterThanTest::testFailure
Failed asserting that 1 is greater than 2.

/home/sb/GreaterThanTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertGreaterThanOrEqual()

assertGreaterThanOrEqual(mixed $expected, mixed $actual[, string $message = ''])

Reporta um erro identificado pela $message se o valor de $actual não é maior que ou igual ao valor de $expected.

assertAttributeGreaterThanOrEqual() é um invólucro conveniente que usa um atributo public, protected, ou private de uma classe ou objeto como o valor atual.

Exemplo A.22: Utilização de assertGreaterThanOrEqual()

<?php
class GreatThanOrEqualTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertGreaterThanOrEqual(2, 1);
    }
}
?>
phpunit GreaterThanOrEqualTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) GreatThanOrEqualTest::testFailure
Failed asserting that 1 is equal to 2 or is greater than 2.

/home/sb/GreaterThanOrEqualTest.php:6

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.

assertInstanceOf()

assertInstanceOf($expected, $actual[, $message = ''])

Reporta um erro identificado pela $message se o $actual não é uma instância de $expected.

assertNotInstanceOf() é o inverso desta asserção e recebe os mesmos argumentos.

assertAttributeInstanceOf() e assertAttributeNotInstanceOf() são invólucros convenientes que podem ser aplicados a um atributo public, protected, ou private de uma classe ou objeto.

Exemplo A.23: Utilização de assertInstanceOf()

<?php
class InstanceOfTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertInstanceOf('RuntimeException', new Exception);
    }
}
?>
phpunit InstanceOfTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) InstanceOfTest::testFailure
Failed asserting that Exception Object (...) is an instance of class "RuntimeException".

/home/sb/InstanceOfTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertInternalType()

assertInternalType($expected, $actual[, $message = ''])

Reporta um erro identificado pela $message se o $actual não é do tipo $expected.

assertNotInternalType() é o inverso desta asserção e recebe os mesmos argumentos.

assertAttributeInternalType() e assertAttributeNotInternalType() são invólucros convenientes que podem aplicados a um atributo public, protected, ou private de uma classe ou objeto.

Exemplo A.24: Utilização de assertInternalType()

<?php
class InternalTypeTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertInternalType('string', 42);
    }
}
?>
phpunit InternalTypeTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) InternalTypeTest::testFailure
Failed asserting that 42 is of type "string".

/home/sb/InternalTypeTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertJsonFileEqualsJsonFile()

assertJsonFileEqualsJsonFile(mixed $expectedFile, mixed $actualFile[, string $message = ''])

Reporta um erro identificado pela $message se o valor de $actualFile não combina com o valor de $expectedFile.

Exemplo A.25: Utilização de assertJsonFileEqualsJsonFile()

<?php
class JsonFileEqualsJsonFileTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertJsonFileEqualsJsonFile(
          'path/to/fixture/file', 'path/to/actual/file');
    }
}
?>
phpunit JsonFileEqualsJsonFileTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) JsonFileEqualsJsonFile::testFailure
Failed asserting that '{"Mascot":"Tux"}' matches JSON string "["Mascott", "Tux", "OS", "Linux"]".

/home/sb/JsonFileEqualsJsonFileTest.php:5

FAILURES!
Tests: 1, Assertions: 3, Failures: 1.

assertJsonStringEqualsJsonFile()

assertJsonStringEqualsJsonFile(mixed $expectedFile, mixed $actualJson[, string $message = ''])

Reporta um erro identificado pela $message se o valor de $actualJson não combina com o valor de $expectedFile.

Exemplo A.26: Utilização de assertJsonStringEqualsJsonFile()

<?php
class JsonStringEqualsJsonFileTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertJsonStringEqualsJsonFile(
          'path/to/fixture/file', json_encode(array("Mascot" => "ux"))
        );
    }
}
?>
phpunit JsonStringEqualsJsonFileTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) JsonStringEqualsJsonFile::testFailure
Failed asserting that '{"Mascot":"ux"}' matches JSON string "{"Mascott":"Tux"}".

/home/sb/JsonStringEqualsJsonFileTest.php:5

FAILURES!
Tests: 1, Assertions: 3, Failures: 1.

assertJsonStringEqualsJsonString()

assertJsonStringEqualsJsonString(mixed $expectedJson, mixed $actualJson[, string $message = ''])

Reporta um erro identificado pela $message se o valor de $actualJson não combina com o valor de $expectedJson.

Exemplo A.27: Utilização de assertJsonStringEqualsJsonString()

<?php
class JsonStringEqualsJsonStringTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertJsonStringEqualsJsonString(
          json_encode(array("Mascot" => "Tux")), json_encode(array("Mascott" => "ux"))
        );
    }
}
?>
phpunit JsonStringEqualsJsonStringTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) JsonStringEqualsJsonStringTest::testFailure
Failed asserting that two objects are equal.
--- Expected
+++ Actual
@@ @@
 stdClass Object (
 -    'Mascot' => 'Tux'
 +    'Mascot' => 'ux'
)

/home/sb/JsonStringEqualsJsonStringTest.php:5

FAILURES!
Tests: 1, Assertions: 3, Failures: 1.

assertLessThan()

assertLessThan(mixed $expected, mixed $actual[, string $message = ''])

Reporta um erro identificado pela $message se o valor de $actual não é menor que o valor de $expected.

assertAttributeLessThan() é um invólucro conveniente que usa um atributo public, protected, ou private de uma classe ou objeto como o valor atual.

Exemplo A.28: Utilização de assertLessThan()

<?php
class LessThanTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertLessThan(1, 2);
    }
}
?>
phpunit LessThanTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) LessThanTest::testFailure
Failed asserting that 2 is less than 1.

/home/sb/LessThanTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertLessThanOrEqual()

assertLessThanOrEqual(mixed $expected, mixed $actual[, string $message = ''])

Reporta um erro identificado pela $message se o valor de $actual não é menor que ou igual ao valor de $expected.

assertAttributeLessThanOrEqual() é um invólucro conveniente que usa um atributo public, protected, ou private de uma classe ou objeto como o valor atual.

Exemplo A.29: Utilização de assertLessThanOrEqual()

<?php
class LessThanOrEqualTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertLessThanOrEqual(1, 2);
    }
}
?>
phpunit LessThanOrEqualTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) LessThanOrEqualTest::testFailure
Failed asserting that 2 is equal to 1 or is less than 1.

/home/sb/LessThanOrEqualTest.php:6

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.

assertNull()

assertNull(mixed $variable[, string $message = ''])

Reporta um erro identificado pela $message se o $variable não é NULL.

assertNotNull() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.30: Utilização de assertNull()

<?php
class NullTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertNull('foo');
    }
}
?>
phpunit NotNullTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) NullTest::testFailure
Failed asserting that 'foo' is null.

/home/sb/NotNullTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertObjectHasAttribute()

assertObjectHasAttribute(string $attributeName, object $object[, string $message = ''])

Reporta um erro identificado pela $message se o $object->attributeName não existir.

assertObjectNotHasAttribute() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.31: Utilização de assertObjectHasAttribute()

<?php
class ObjectHasAttributeTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertObjectHasAttribute('foo', new stdClass);
    }
}
?>
phpunit ObjectHasAttributeTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 4.75Mb

There was 1 failure:

1) ObjectHasAttributeTest::testFailure
Failed asserting that object of class "stdClass" has attribute "foo".

/home/sb/ObjectHasAttributeTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertRegExp()

assertRegExp(string $pattern, string $string[, string $message = ''])

Reporta um erro identificado pela $message se a $string não combina com a expressão regular $pattern.

assertNotRegExp() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.32: Utilização de assertRegExp()

<?php
class RegExpTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertRegExp('/foo/', 'bar');
    }
}
?>
phpunit RegExpTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) RegExpTest::testFailure
Failed asserting that 'bar' matches PCRE pattern "/foo/".

/home/sb/RegExpTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertStringMatchesFormat()

assertStringMatchesFormat(string $format, string $string[, string $message = ''])

Reporta um erro identificado pela $message se a $string não combina com a string $format.

assertStringNotMatchesFormat() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.33: Utilização de assertStringMatchesFormat()

<?php
class StringMatchesFormatTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertStringMatchesFormat('%i', 'foo');
    }
}
?>
phpunit StringMatchesFormatTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) StringMatchesFormatTest::testFailure
Failed asserting that 'foo' matches PCRE pattern "/^[+-]?\d+$/s".

/home/sb/StringMatchesFormatTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

A string de formato pode conter os seguintes substitutos (placeholders):

  • %e: Representa um separador de diretório, por exemplo / no Linux.

  • %s: Um ou mais de qualquer coisa (caractere ou espaço em branco) exceto o caractere de fim de linha.

  • %S: Zero ou mais de qualquer coisa (caractere ou espaço em branco) exceto o caractere de fim de linha.

  • %a: Um ou mais de qualquer coisa (caractere ou espaço em branco) incluindo o caractere de fim de linha.

  • %A: Zero ou mais de qualquer coisa (caractere ou espaço em branco) incluindo o caractere de fim de linha.

  • %w: Zero ou mais caracteres de espaços em branco.

  • %i: Um valor inteiro assinado, por exemplo +3142, -3142.

  • %d: Um valor inteiro não-assinado, por exemplo 123456.

  • %x: Um ou mais caracteres hexadecimais. Isto é, caracteres na classe 0-9, a-f, A-F.

  • %f: Um número de ponto flutuante, por exemplo: 3.142, -3.142, 3.142E-10, 3.142e+10.

  • %c: Um único caractere de qualquer ordem.

assertStringMatchesFormatFile()

assertStringMatchesFormatFile(string $formatFile, string $string[, string $message = ''])

Reporta um erro identificado pela $message se a $string não combina com o conteúdo do $formatFile.

assertStringNotMatchesFormatFile() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.34: Utilização de assertStringMatchesFormatFile()

<?php
class StringMatchesFormatFileTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertStringMatchesFormatFile('/path/to/expected.txt', 'foo');
    }
}
?>
phpunit StringMatchesFormatFileTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) StringMatchesFormatFileTest::testFailure
Failed asserting that 'foo' matches PCRE pattern "/^[+-]?\d+
$/s".

/home/sb/StringMatchesFormatFileTest.php:6

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.

assertSame()

assertSame(mixed $expected, mixed $actual[, string $message = ''])

Reporta um erro identificado pela $message se as variáveis $expected e $actual não tem o mesmo tipo e valor.

assertNotSame() é o inverso desta asserção e recebe os mesmos argumentos.

assertAttributeSame() e assertAttributeNotSame() são invólucros convenientes que usam um atributo public, protected, ou private de uma classe ou objeto como o valor atual.

Exemplo A.35: Utilização de assertSame()

<?php
class SameTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertSame('2204', 2204);
    }
}
?>
phpunit SameTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) SameTest::testFailure
Failed asserting that 2204 is identical to '2204'.

/home/sb/SameTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertSame(object $expected, object $actual[, string $message = ''])

Reporta um erro identificado pela $message se as variáveis $expected e $actual não referenciam o mesmo objeto.

Exemplo A.36: Utilização de assertSame() com objetos

<?php
class SameTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertSame(new stdClass, new stdClass);
    }
}
?>
phpunit SameTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 4.75Mb

There was 1 failure:

1) SameTest::testFailure
Failed asserting that two variables reference the same object.

/home/sb/SameTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertStringEndsWith()

assertStringEndsWith(string $suffix, string $string[, string $message = ''])

Reporta um erro identificado pela $message se a $string não termina com $suffix.

assertStringEndsNotWith() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.37: Utilização de assertStringEndsWith()

<?php
class StringEndsWithTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertStringEndsWith('suffix', 'foo');
    }
}
?>
phpunit StringEndsWithTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 1 second, Memory: 5.00Mb

There was 1 failure:

1) StringEndsWithTest::testFailure
Failed asserting that 'foo' ends with "suffix".

/home/sb/StringEndsWithTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertStringEqualsFile()

assertStringEqualsFile(string $expectedFile, string $actualString[, string $message = ''])

Reporta um erro identificado pela $message se o arquivo especificado por $expectedFile não tem $actualString com seu conteúdo.

assertStringNotEqualsFile() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.38: Utilização de assertStringEqualsFile()

<?php
class StringEqualsFileTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertStringEqualsFile('/home/sb/expected', 'actual');
    }
}
?>
phpunit StringEqualsFileTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) StringEqualsFileTest::testFailure
Failed asserting that two strings are equal.
--- Expected
+++ Actual
@@ @@
-'expected
-'
+'actual'

/home/sb/StringEqualsFileTest.php:6

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.

assertStringStartsWith()

assertStringStartsWith(string $prefix, string $string[, string $message = ''])

Reporta um erro identificado pela $message se a $string não inicia com $prefix.

assertStringStartsNotWith() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.39: Utilização de assertStringStartsWith()

<?php
class StringStartsWithTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertStringStartsWith('prefix', 'foo');
    }
}
?>
phpunit StringStartsWithTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) StringStartsWithTest::testFailure
Failed asserting that 'foo' starts with "prefix".

/home/sb/StringStartsWithTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertThat()

Asserções mais complexas podem ser formuladas usando as classes PHPUnit_Framework_Constraint. Elas podem ser avaliadas usando o método assertThat(). Exemplo A.40 mostra como as restrições logicalNot() e equalTo() podem ser usadas para expressar a mesma asserção como assertNotEquals().

assertThat(mixed $value, PHPUnit_Framework_Constraint $constraint[, $message = ''])

Reporta um erro identificado pela $message se o$value não combina com a $constraint.

Exemplo A.40: Utilização de assertThat()

<?php
class BiscuitTest extends PHPUnit_Framework_TestCase
{
    public function testEquals()
    {
        $theBiscuit = new Biscuit('Ginger');
        $myBiscuit  = new Biscuit('Ginger');

        $this->assertThat(
          $theBiscuit,
          $this->logicalNot(
            $this->equalTo($myBiscuit)
          )
        );
    }
}
?>

Tabela A.1 mostra as classes PHPUnit_Framework_Constraint disponíveis.

Tabela A.1. Restrições

RestriçãoSignificado
PHPUnit_Framework_Constraint_Attribute attribute(PHPUnit_Framework_Constraint $constraint, $attributeName)Restrição que aplica outra restrição a um atributo de uma classe ou objeto.
PHPUnit_Framework_Constraint_IsAnything anything()Restrição que aceita qualquer valor de entrada.
PHPUnit_Framework_Constraint_ArrayHasKey arrayHasKey(mixed $key)Restrição que assevera que o array foi avaliado a ter uma chave fornecida.
PHPUnit_Framework_Constraint_TraversableContains contains(mixed $value)Restrição que assevera que o array ou objeto que implementa a interface Iterator foi avaliado para conter um valor fornecido.
PHPUnit_Framework_Constraint_TraversableContainsOnly containsOnly(string $type)Restrição que assevera que o array ou objeto que implementa a interface Iterator foi avaliado para conter somente valores de um tipo fornecido.
PHPUnit_Framework_Constraint_TraversableContainsOnly containsOnlyInstancesOf(string $classname)Restrição que assevera que o array ou objeto que implementa a interface Iterator foi avaliado para conter somente instâncias de um nome de classe fornecido.
PHPUnit_Framework_Constraint_IsEqual equalTo($value, $delta = 0, $maxDepth = 10)Restrição que verificar se um valor é igual a outro.
PHPUnit_Framework_Constraint_Attribute attributeEqualTo($attributeName, $value, $delta = 0, $maxDepth = 10)Restrição que verificar se um valor é igual a um atributo de uma classe ou de um objeto.
PHPUnit_Framework_Constraint_FileExists fileExists()Restrição que verificar se o arquivo(nome) que foi avaliado existe.
PHPUnit_Framework_Constraint_GreaterThan greaterThan(mixed $value)Restrição que assevera que o valor que foi avaliado é maior que um valor fornecido.
PHPUnit_Framework_Constraint_Or greaterThanOrEqual(mixed $value)Restrição que assevera que o valor que foi avaliado é maior ou igual a um valor fornecido.
PHPUnit_Framework_Constraint_ClassHasAttribute classHasAttribute(string $attributeName)Restrição que assevera que a classe que foi avaliada tem um atributo fornecido.
PHPUnit_Framework_Constraint_ClassHasStaticAttribute classHasStaticAttribute(string $attributeName)Restrição que assevera que a classe que foi avaliada tem um atributo estático fornecido.
PHPUnit_Framework_Constraint_ObjectHasAttribute hasAttribute(string $attributeName)Restrição que assevera que o objeto que foi avaliado tem um atributo fornecido.
PHPUnit_Framework_Constraint_IsIdentical identicalTo(mixed $value)Restrição que assevera que um valor é idêntico a outro.
PHPUnit_Framework_Constraint_IsFalse isFalse()Restrição que assevera que o valor que foi avaliado é FALSE.
PHPUnit_Framework_Constraint_IsInstanceOf isInstanceOf(string $className)Restrição que assevera que o objeto que foi avaliado é uma instância de uma classe fornecida.
PHPUnit_Framework_Constraint_IsNull isNull()Restrição que assevera que o valor que foi avaliado é NULL.
PHPUnit_Framework_Constraint_IsTrue isTrue()Restrição que assevera que o valor que foi avaliado é TRUE.
PHPUnit_Framework_Constraint_IsType isType(string $type)Restrição que assevera que o valor que foi avaliado é de um tipo especificado.
PHPUnit_Framework_Constraint_LessThan lessThan(mixed $value)Restrição que assevera que o valor que foi avaliado é menor que o valor fornecido.
PHPUnit_Framework_Constraint_Or lessThanOrEqual(mixed $value)Restrição que assevera que o valor que foi avaliado é menor ou igual ao valor fornecido.
logicalAnd()Lógico AND.
logicalNot(PHPUnit_Framework_Constraint $constraint)Lógico NOT.
logicalOr()Lógico OR.
logicalXor()Lógico XOR.
PHPUnit_Framework_Constraint_PCREMatch matchesRegularExpression(string $pattern)Restrição que assevera que a string que foi avaliada combina com uma expressão regular.
PHPUnit_Framework_Constraint_StringContains stringContains(string $string, bool $case)Restrição que assevera que a string que foi avaliada contém uma string fornecida
PHPUnit_Framework_Constraint_StringEndsWith stringEndsWith(string $suffix)Restrição que assevera que a string que foi avaliada termina com um sufixo fornecido.
PHPUnit_Framework_Constraint_StringStartsWith stringStartsWith(string $prefix)Restrição que assevera que a string que foi avaliada começa com um prefixo fornecido.

assertTrue()

assertTrue(bool $condition[, string $message = ''])

Reporta um erro identificado pela $message se o $condition é FALSE.

assertNotTrue() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.41: Utilização de assertTrue()

<?php
class TrueTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertTrue(FALSE);
    }
}
?>
phpunit TrueTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) TrueTest::testFailure
Failed asserting that false is true.

/home/sb/TrueTest.php:6

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

assertXmlFileEqualsXmlFile()

assertXmlFileEqualsXmlFile(string $expectedFile, string $actualFile[, string $message = ''])

Reporta um erro identificado pela $message se o documento XML em $actualFile não é igual ao documento XML em $expectedFile.

assertXmlFileNotEqualsXmlFile() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.42: Utilização de assertXmlFileEqualsXmlFile()

<?php
class XmlFileEqualsXmlFileTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertXmlFileEqualsXmlFile(
          '/home/sb/expected.xml', '/home/sb/actual.xml');
    }
}
?>
phpunit XmlFileEqualsXmlFileTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) XmlFileEqualsXmlFileTest::testFailure
Failed asserting that two DOM documents are equal.
--- Expected
+++ Actual
@@ @@
 <?xml version="1.0"?>
 <foo>
-  <bar/>
+  <baz/>
 </foo>

/home/sb/XmlFileEqualsXmlFileTest.php:7

FAILURES!
Tests: 1, Assertions: 3, Failures: 1.

assertXmlStringEqualsXmlFile()

assertXmlStringEqualsXmlFile(string $expectedFile, string $actualXml[, string $message = ''])

Reporta um erro identificado pela $message se o documento XML em $actualXml não é igual ao documento XML em $expectedFile.

assertXmlStringNotEqualsXmlFile() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.43: Utilização de assertXmlStringEqualsXmlFile()

<?php
class XmlStringEqualsXmlFileTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertXmlStringEqualsXmlFile(
          '/home/sb/expected.xml', '<foo><baz/></foo>');
    }
}
?>
phpunit XmlStringEqualsXmlFileTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) XmlStringEqualsXmlFileTest::testFailure
Failed asserting that two DOM documents are equal.
--- Expected
+++ Actual
@@ @@
 <?xml version="1.0"?>
 <foo>
-  <bar/>
+  <baz/>
 </foo>

/home/sb/XmlStringEqualsXmlFileTest.php:7

FAILURES!
Tests: 1, Assertions: 2, Failures: 1.

assertXmlStringEqualsXmlString()

assertXmlStringEqualsXmlString(string $expectedXml, string $actualXml[, string $message = ''])

Reporta um erro identificado pela $message se o documento XML em $actualXml não é igual ao documento XML em $expectedXml.

assertXmlStringNotEqualsXmlString() é o inverso desta asserção e recebe os mesmos argumentos.

Exemplo A.44: Utilização de assertXmlStringEqualsXmlString()

<?php
class XmlStringEqualsXmlStringTest extends PHPUnit_Framework_TestCase
{
    public function testFailure()
    {
        $this->assertXmlStringEqualsXmlString(
          '<foo><bar/></foo>', '<foo><baz/></foo>');
    }
}
?>
phpunit XmlStringEqualsXmlStringTest
PHPUnit 4.7.0 by Sebastian Bergmann and contributors.

F

Time: 0 seconds, Memory: 5.00Mb

There was 1 failure:

1) XmlStringEqualsXmlStringTest::testFailure
Failed asserting that two DOM documents are equal.
--- Expected
+++ Actual
@@ @@
 <?xml version="1.0"?>
 <foo>
-  <bar/>
+  <baz/>
 </foo>

/home/sb/XmlStringEqualsXmlStringTest.php:7

FAILURES!
Tests: 1, Assertions: 1, Failures: 1.

Apêndice B. Anotações

Uma anotação é uma forma especial de metadados sintáticos que podem ser adicionados ao código-fonte de algumas linguagens de programação. Enquanto o PHP não tem um recurso de linguagem dedicado a anotação de código-fonte, o uso de tags como @annotation arguments em bloco de documentação tem sido estabelecido na comunidade do PHP para anotar o código-fonte. Os blocos de documentação PHP são reflexivos: eles podem ser acessados através do método getDocComment() da API Reflection a nível de função, classe, método e atributo. Aplicações como o PHPUnit usam essa informação em tempo de execução para configurar seu comportamento.

Nota

Um comentário de documentação em PHP deve começar com /** e terminar com */. Anotações em algum outro estilo de comentário será ignorada.

Este apêndice mostra todas as variedades de anotações suportadas pelo PHPUnit.

@author

A anotação @author é um apelido para a anotação @group (veja “@group”) e permite filtrar os testes baseado em seus autores.

@after

A anotação @after pode ser usada para especificar métodos que devem ser chamados depois de cada método em uma classe de caso de teste.

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @after
     */
    public function tearDownSomeFixtures()
    {
        // ...
    }

    /**
     * @after
     */
    public function tearDownSomeOtherFixtures()
    {
        // ...
    }
}

@afterClass

A anotação @afterClass pode ser usada para especificar métodos estáticos que devem ser chamados depois de todos os métodos de teste em uma classe de teste foram executados para limpar ambientes compartilhados.

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @afterClass
     */
    public static function tearDownSomeSharedFixtures()
    {
        // ...
    }

    /**
     * @afterClass
     */
    public static function tearDownSomeOtherSharedFixtures()
    {
        // ...
    }
}

@backupGlobals

As operações de cópia de segurança e restauração para variáveis globais podem ser completamente desabilitadas para todos os testes de uma classe de caso de teste como esta

/**
 * @backupGlobals disabled
 */
class MyTest extends PHPUnit_Framework_TestCase
{
    // ...
}

A anotação @backupGlobals também pode ser usada a nível de método de teste. Isso permite uma configuração refinada das operações de cópia de segurança e restauração:

/**
 * @backupGlobals disabled
 */
class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @backupGlobals enabled
     */
    public function testThatInteractsWithGlobalVariables()
    {
        // ...
    }
}

@backupStaticAttributes

A anotação @backupStaticAttributes pode ser usada para copiar todos valores de propriedades estáticas em todas classes declaradas antes de cada teste e restaurá-los depois. Pode ser usado em nível de classe de caso de teste ou método de teste:

/**
 * @backupStaticAttributes enabled
 */
class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @backupStaticAttributes disabled
     */
    public function testThatInteractsWithStaticAttributes()
    {
        // ...
    }
}

Nota

@backupStaticAttributes é limitada pela parte interna do PHP e pode causar valores estáticos não intencionais ao persistir e vazar para testes subsequentes em algumas circunstâncias.

Veja “Estado Global” para detalhes.

@before

A anotação @before pode ser usada para especificar métodos que devem ser chamados antes de cada método de teste em uma classe de caso de teste.

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @before
     */
    public function setupSomeFixtures()
    {
        // ...
    }

    /**
     * @before
     */
    public function setupSomeOtherFixtures()
    {
        // ...
    }
}

@beforeClass

A anotação @beforeClass pode ser usada para especificar métodos estáticos que devem ser chamados antes de quaisquer métodos de teste em uma classe de teste serem executados para criar ambientes compartilahdos.

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @beforeClass
     */
    public static function setUpSomeSharedFixtures()
    {
        // ...
    }

    /**
     * @beforeClass
     */
    public static function setUpSomeOtherSharedFixtures()
    {
        // ...
    }
}

@codeCoverageIgnore*

As anotações @codeCoverageIgnore, @codeCoverageIgnoreStart e @codeCoverageIgnoreEnd podem ser usadas para excluir linhas de código da análise de cobertura.

Para uso, veja “Ignorando Blocos de Código”.

@covers

A anotação @covers pode ser usada no código de teste para especificar quais métodos um método de teste quer testar:

/**
 * @covers BankAccount::getBalance
 */
public function testBalanceIsInitiallyZero()
{
    $this->assertEquals(0, $this->ba->getBalance());
}

Se fornecida, apenas a informação de cobertura de código para o(s) método(s) especificado(s) será considerada.

Tabela B.1 mostra a sintaxe da anotação @covers.

Tabela B.1. Anotações para especificar quais métodos são cobertos por um teste

AnotaçãoDescrição
@covers ClassName::methodNameEspecifica que o método de teste anotado cobre o método especificado.
@covers ClassNameEspecifica que o método de teste anotado cobre todos os métodos de uma dada classe.
@covers ClassName<extended>Especifica que o método de teste anotado cobre todos os métodos de uma dada classe e sua(s) classe(s) pai(s) e interface(s).
@covers ClassName::<public>Especifica que o método de teste anotado cobre todos os métodos públicos de uma dada classe.
@covers ClassName::<protected>Especifica que o método de teste anotado cobre todos os métodos protegidos de uma dada classe.
@covers ClassName::<private>Especifica que o método de teste anotado cobre todos os métodos privados de uma dada classe.
@covers ClassName::<!public>Especifica que o método de teste anotado cobre todos os métodos que não sejam públicos de uma dada classe.
@covers ClassName::<!protected>Especifica que o método de teste anotado cobre todos os métodos que não sejam protegidos de uma dada classe.
@covers ClassName::<!private>Especifica que o método de teste anotado cobre todos os métodos que não sejam privados de uma dada classe.
@covers ::functionNameEspecifica que o método de teste anotado cobre todos os métodos que não sejam privados de uma dada classe.

@coversDefaultClass

A anotação @coversDefaultClass pode ser usada para especificar um namespace padrão ou nome de classe. Dessa forma nomes longos não precisam ser repetidos para toda anotação @covers. Veja o Exemplo B.1.

Exemplo B.1: Usando @coversDefaultClass para encurtar anotações

<?php
/**
 * @coversDefaultClass \Foo\CoveredClass
 */
class CoversDefaultClassTest extends PHPUnit_Framework_TestCase
{
    /**
     * @covers ::publicMethod
     */
    public function testSomething()
    {
        $o = new Foo\CoveredClass;
        $o->publicMethod();
    }
}
?>

@coversNothing

A anotação @coversNothing pode ser usada no código de teste para especificar que nenhuma informação de cobertura de código será gravada para o caso de teste anotado.

Isso pode ser usado para testes de integração. Veja Exemplo 11.3 para um exemplo.

A anotação pode ser usada nos níveis de classe e de método e vão sobrescrever quaisquer tags @covers.

@dataProvider

Um método de teste pode aceitar argumentos arbitrários. Esses argumentos devem ser fornecidos por um método provedor (provider() em Exemplo 2.5). O método provedor de dados a ser usado é especificado usando a anotação @dataProvider.

Veja “Provedores de Dados” para mais detalhes.

@depends

O PHPUnit suporta a declaração de dependências explícitas entre métodos de teste. Tais dependências não definem a ordem em que os métodos de teste devem ser executados, mas permitem o retorno de uma instância do ambiente de teste por um produtor e passá-la aos consumidores dependentes. O Exemplo 2.2 mostra como usar a anotação @depends para expressar dependências entre métodos de teste.

Veja “Dependências de Testes” para mais detalhes.

@expectedException

O Exemplo 2.9 mostra como usar a anotação @expectedException para testar se uma exceção é lançada dentro do código testado.

Veja “Testando Exceções” para mais detalhes.

@expectedExceptionCode

A anotação @expectedExceptionCode em conjunção com a @expectedException permite fazer asserções no código de erro de uma exceção lançada, permitindo diminuir uma exceção específica.

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException     MyException
     * @expectedExceptionCode 20
     */
    public function testExceptionHasErrorcode20()
    {
        throw new MyException('Some Message', 20);
    }
}

Para facilitar o teste e reduzir a duplicação, um atalho pode ser usado para especificar uma constante de classe como um @expectedExceptionCode usando a sintaxe "@expectedExceptionCode ClassName::CONST".

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
      * @expectedException     MyException
      * @expectedExceptionCode MyClass::ERRORCODE
      */
    public function testExceptionHasErrorcode20()
    {
      throw new MyException('Some Message', 20);
    }
}
class MyClass
{
    const ERRORCODE = 20;
}

@expectedExceptionMessage

A anotação @expectedExceptionMessage trabalha de modo similar a @expectedExceptionCode já que lhe permite fazer uma asserção na mensagem de erro de uma exceção.

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @expectedException        MyException
     * @expectedExceptionMessage Some Message
     */
    public function testExceptionHasRightMessage()
    {
        throw new MyException('Some Message', 20);
    }
}

A mensagem esperada pode ser uma substring de uma Mensagem de exceção. Isso pode ser útil para asseverar apenas que um certo nome ou parâmetro que foi passado é mostrado na exceção e não fixar toda a mensagem de exceção no teste.

class MyTest extends PHPUnit_Framework_TestCase
{
     /**
      * @expectedException        MyException
      * @expectedExceptionMessage broken
      */
     public function testExceptionHasRightMessage()
     {
         $param = "broken";
         throw new MyException('Invalid parameter "'.$param.'".', 20);
     }
}

Para facilitar o teste e reduzir duplicação um atalho pode ser usado para especificar uma constante de classe como uma @expectedExceptionMessage usando a sintaxe "@expectedExceptionMessage ClassName::CONST". Um exemplo pode ser encontrado na seção chamada “@expectedExceptionCode”.

@expectedExceptionMessageRegExp

A mensagem experada também pode ser especificada como uma expressão regular usando a anotação @expectedExceptionMessageRegExp. Isso é útil em situações aonde uma substring não é adequada para combinar uma determinada mensagem.

class MyTest extends PHPUnit_Framework_TestCase
{
     /**
      * @expectedException              MyException
      * @expectedExceptionMessageRegExp /Argument \d+ can not be an? \w+/
      */
     public function testExceptionHasRightMessage()
     {
         throw new MyException('Argument 2 can not be an integer');
     }
}

@group

Um teste pode ser marcado como pertencente a um ou mais grupos usando a anotação @group desta forma

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @group specification
     */
    public function testSomething()
    {
    }

    /**
     * @group regresssion
     * @group bug2204
     */
    public function testSomethingElse()
    {
    }
}

Testes podem ser selecionados para execução baseada em grupos usando as opções --group e --exclude-group do executor de teste em linha-de-comando ou usando as respectivas diretivas do arquivo de configuração XML.

@large

A anotação @large é um apelido para @group large.

Se o pacote PHP_Invoker estiver instalado e o modo estrito estiver habilitado, um teste grande irá falhar se ele demorar mais de 60 segundos para executar. Esse tempo de espera é configurável através do atributo timeoutForLargeTests no arquivo de configuração XML.

@medium

A anotação @medium é um apelido para @group medium. Um teste médio não deve depender de um teste marcado como @large.

Se o pacote PHP_Invoker estiver instalado e o modo estrito estiver habilitado, um teste médio irá falhar se ele demorar mais de 10 segundos para executar. Esse tempo de espera é configurável através do atributo timeoutForMediumTests no arquivo de configuração XML.

@preserveGlobalState

Quando um teste é executado em um processo separado, o PHPUnit tentará preservar o estado global de processo pai serializando todos globais no processo pai e deserializando-os no processo filho. Isso pode causar problemas se o processo pai contém globais que não são serializáveis. Para corrigir isto, você pode prevenir o PHPUnit de preservar o estado global com a anotação @preserveGlobalState.

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @runInSeparateProcess
     * @preserveGlobalState disabled
     */
    public function testInSeparateProcess()
    {
        // ...
    }
}

@requires

A anotação @requires pode ser usada para pular testes quando pré-condições comuns, como a Versão do PHP ou extensões instaladas, não batem.

Uma lista completa de possibilidades e exemplos pode ser encontrada em Tabela 7.3

@runTestsInSeparateProcesses

Indica que todos testes em uma classe de teste devem ser executados em um processo PHP separado.

/**
 * @runTestsInSeparateProcesses
 */
class MyTest extends PHPUnit_Framework_TestCase
{
    // ...
}

Nota: Por padrão, o PHPUnit tentará preservar o estado global de processo pai serializando todos globais no processo pai e deserializando-os no processo filho. Isso pode causar problemas se o processo pai contém globais que não são serializáveis. Veja “@preserveGlobalState” para informações de como corrigir isso.

@runInSeparateProcess

Indica que um teste deve ser executado em um processo PHP separado.

class MyTest extends PHPUnit_Framework_TestCase
{
    /**
     * @runInSeparateProcess
     */
    public function testInSeparateProcess()
    {
        // ...
    }
}

Nota: Por padrão, o PHPUnit tentará preservar o estado global de processo pai serializando todos globais no processo pai e deserializando-os no processo filho. Isso pode causar problemas se o processo pai contém globais que não são serializáveis. Veja “@preserveGlobalState” para informações de como corrigir isso.

@small

A anotação @small é um apelido para @group small. Um teste pequeno não deve depender de um teste marcado como @medium ou @large.

Se o pacote PHP_Invoker estiver instalado e o modo estrito estiver habilitado, um teste pequeno irá falhar se ele demorar mais de 1 segundo para executar. Esse tempo de espera é configurável através do atributo timeoutForLargeTests no arquivo de configuração XML.

Nota

Pr padrão, todos testes são considerados pequenos se eles não são como @medium ou @large. Note, por favor, no entanto, que --group e as opções relacionadas irão apenas considerarão um teste ser do grupo small se ele é marcado explicitamente com a anotação apropriada.

@test

Como uma alternativa para prefixar seus nomes de métodos de teste com test, você pode usar a anotação @test no Bloco de Documentação do método para marcá-lo como um método de teste.

/**
 * @test
 */
public function initialBalanceShouldBe0()
{
    $this->assertEquals(0, $this->ba->getBalance());
}

@testdox

@ticket

@uses

A anotação @uses especifica o código que irá ser executado por um teste, mas não se destina a ser coberto pelo teste. Um bom exemplo é um objeto valor que é necessário para testar um unidade de código.

/**
 * @covers BankAccount::deposit
 * @uses   Money
 */
public function testMoneyCanBeDepositedInAccount()
{
    // ...
}

Essa anotação é especialmente útil em modo de cobertura estrita aonde código coberto involuntariamente fará um teste falhar. Veja “Cobertura de Código Involuntária” para mais informação sobre modo de cobertura estrito.

Apêndice C. O arquivo de configuração XML

PHPUnit

Os atributos do elemento <phpunit> podem ser usados para configurar a funcionalidade principal do PHPUnit.

<phpunit
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.6/phpunit.xsd"
         backupGlobals="true"
         backupStaticAttributes="false"
         <!--bootstrap="/path/to/bootstrap.php"-->
         cacheTokens="false"
         colors="false"
         convertErrorsToExceptions="true"
         convertNoticesToExceptions="true"
         convertWarningsToExceptions="true"
         forceCoversAnnotation="false"
         mapTestClassNameToCoveredClassName="false"
         printerClass="PHPUnit_TextUI_ResultPrinter"
         <!--printerFile="/path/to/ResultPrinter.php"-->
         processIsolation="false"
         stopOnError="false"
         stopOnFailure="false"
         stopOnIncomplete="false"
         stopOnSkipped="false"
         testSuiteLoaderClass="PHPUnit_Runner_StandardTestSuiteLoader"
         <!--testSuiteLoaderFile="/path/to/StandardTestSuiteLoader.php"-->
         timeoutForSmallTests="1"
         timeoutForMediumTests="10"
         timeoutForLargeTests="60"
         verbose="false">
  <!-- ... -->
</phpunit>

A configuração XML acima corresponde ao comportamento padrão do executor de teste TextUI documentado na “Opções de linha-de-comando”.

Opções adicionais que não estão disponíveis como opções em linha-de-comando são:

convertErrorsToExceptions

Por padrão, PHPUnit irá instalar um manipulador de erro que converte os seguintes erros para exceções:

  • E_WARNING
  • E_NOTICE
  • E_USER_ERROR
  • E_USER_WARNING
  • E_USER_NOTICE
  • E_STRICT
  • E_RECOVERABLE_ERROR
  • E_DEPRECATED
  • E_USER_DEPRECATED

Definir convertErrorsToExceptions para false desabilita esta funcionalidade.

convertNoticesToExceptions

Quando definido para false, o manipulador de erro instalado pelo convertErrorsToExceptions não irá converter os erros E_NOTICE, E_USER_NOTICE, ou E_STRICT para exceções.

convertWarningsToExceptions

Quando definido para false, o manipulador de erro instalado pelo convertErrorsToExceptions não irá converter os erros E_WARNING ou E_USER_WARNING para exceções.

forceCoversAnnotation

A Cobertura de Código só será gravada para testes que usem a anotação @covers documentada na “@covers”.

timeoutForLargeTests

Se o limite de tempo baseado no tamanho do teste são forçados então esse atributo define o tempo limite para todos os testes marcados como @large. Se um teste não completa dentro do tempo limite configurado, irá falhar.

timeoutForMediumTests

Se o limite de tempo baseado no tamanho do teste são forçados então esse atributo define o tempo limite para todos os testes marcados como @medium. Se um teste não completa dentro do tempo limite configurado, irá falhar.

timeoutForSmallTests

Se o limite de tempo baseado no tamanho do teste são forçados então esse atributo define o tempo limite para todos os testes marcados como @medium ou @large. Se um teste não completa dentro do tempo limite configurado, irá falhar.

Suítes de Teste

O elemento <testsuites> e seu(s) um ou mais filhos <testsuite> podem ser usados para compor uma suíte de teste fora das suítes e casos de teste.

<testsuites>
  <testsuite name="My Test Suite">
    <directory>/path/to/*Test.php files</directory>
    <file>/path/to/MyTest.php</file>
    <exclude>/path/to/exclude</exclude>
  </testsuite>
</testsuites>

Usando os atributos phpVersion e phpVersionOperator, uma versão exigida do PHP pode ser especificada. O exemplo abaixo só vai adicionar os arquivos /caminho/para/*Test.php e /caminho/para/MeuTest.php se a versão do PHP for no mínimo 5.3.0.

  <testsuites>
    <testsuite name="My Test Suite">
      <directory suffix="Test.php" phpVersion="5.3.0" phpVersionOperator=">=">/path/to/files</directory>
      <file phpVersion="5.3.0" phpVersionOperator=">=">/path/to/MyTest.php</file>
    </testsuite>
  </testsuites>

O atributo phpVersionOperator é opcional e padronizado para >=.

Grupos

O elemento <groups> e seus filhos <include>, <exclude>, e <group> podem ser usados para selecionar grupos de testes marcados com a anotação @group (documentada na “@group”) que (não) deveriam ser executados.

<groups>
  <include>
    <group>name</group>
  </include>
  <exclude>
    <group>name</group>
  </exclude>
</groups>

A configuração XML acima corresponde a invocar o executor de testes TextUI com as seguintes opções:

  • --group nome

  • --exclude-group nome

Incluindo e Excluindo Arquivos para Cobertura de Código

O elemento <filter> e seus filhos podem ser usados para configurar a lista-negra e lista-branca para o relatório de cobertura de código.

<filter>
  <blacklist>
    <directory suffix=".php">/path/to/files</directory>
    <file>/path/to/file</file>
    <exclude>
      <directory suffix=".php">/path/to/files</directory>
      <file>/path/to/file</file>
    </exclude>
  </blacklist>
  <whitelist processUncoveredFilesFromWhitelist="true">
    <directory suffix=".php">/path/to/files</directory>
    <file>/path/to/file</file>
    <exclude>
      <directory suffix=".php">/path/to/files</directory>
      <file>/path/to/file</file>
    </exclude>
  </whitelist>
</filter>

Registrando

O elemento <logging> e seus filhos <log> podem ser usados para configurar o registro da execução de teste.

<logging>
  <log type="coverage-html" target="/tmp/report" lowUpperBound="35"
       highLowerBound="70"/>
  <log type="coverage-clover" target="/tmp/coverage.xml"/>
  <log type="coverage-php" target="/tmp/coverage.serialized"/>
  <log type="coverage-text" target="php://stdout" showUncoveredFiles="false"/>
  <log type="json" target="/tmp/logfile.json"/>
  <log type="tap" target="/tmp/logfile.tap"/>
  <log type="junit" target="/tmp/logfile.xml" logIncompleteSkipped="false"/>
  <log type="testdox-html" target="/tmp/testdox.html"/>
  <log type="testdox-text" target="/tmp/testdox.txt"/>
</logging>

A configuração XML acima corresponde a invocar o executor de teste TextUI com as seguintes opções:

  • --coverage-html /tmp/report

  • --coverage-clover /tmp/coverage.xml

  • --coverage-php /tmp/coverage.serialized

  • --coverage-text

  • --log-json /tmp/logfile.json

  • > /tmp/logfile.txt

  • --log-tap /tmp/logfile.tap

  • --log-junit /tmp/logfile.xml

  • --testdox-html /tmp/testdox.html

  • --testdox-text /tmp/testdox.txt

Os atributos lowUpperBound, highLowerBound, logIncompleteSkipped e showUncoveredFiles não possuem opção de execução de teste TextUI equivalente.

  • lowUpperBound: Porcentagem máxima de cobertura para ser considerado "moderadamente" coberto.

  • highLowerBound: Porcentagem mínima de cobertura para ser considerado "altamente" coberto.

  • showUncoveredFiles: Mostra todos arquivos da lista-branca na saída --coverage-text não apenas aqueles com informação de cobertura.

  • showOnlySummary: Mostra somente o resumo na saída --coverage-text.

Ouvintes de Teste

O elemento <listeners> e seus filhos <listener> podem ser usados para anexar ouvintes adicionais de teste para a execução dos testes.

<listeners>
  <listener class="MyListener" file="/optional/path/to/MyListener.php">
    <arguments>
      <array>
        <element key="0">
          <string>Sebastian</string>
        </element>
      </array>
      <integer>22</integer>
      <string>April</string>
      <double>19.78</double>
      <null/>
      <object class="stdClass"/>
    </arguments>
  </listener>
</listeners>

A configuração XML acima corresponde a anexar o objeto $listener (veja abaixo) à execução de teste:

$listener = new MyListener(
  array('Sebastian'),
  22,
  'April',
  19.78,
  NULL,
  new stdClass
);

Definindo configurações PHP INI, Constantes e Variáveis Globais

O elemento <php> e seus filhos podem ser usados para definir configurações do PHP, constantes e variáveis globais. Também pode ser usado para preceder o include_path.

<php>
  <includePath>.</includePath>
  <ini name="foo" value="bar"/>
  <const name="foo" value="bar"/>
  <var name="foo" value="bar"/>
  <env name="foo" value="bar"/>
  <post name="foo" value="bar"/>
  <get name="foo" value="bar"/>
  <cookie name="foo" value="bar"/>
  <server name="foo" value="bar"/>
  <files name="foo" value="bar"/>
  <request name="foo" value="bar"/>
</php>

A configuração XML acima corresponde ao seguinte código PHP:

ini_set('foo', 'bar');
define('foo', 'bar');
$GLOBALS['foo'] = 'bar';
$_ENV['foo'] = 'bar';
$_POST['foo'] = 'bar';
$_GET['foo'] = 'bar';
$_COOKIE['foo'] = 'bar';
$_SERVER['foo'] = 'bar';
$_FILES['foo'] = 'bar';
$_REQUEST['foo'] = 'bar';

Configurando Navegadores para Selenium RC

O elemento <selenium> e seus filhos <browser> podem ser usados para configurar uma lista de servidores Selenium RC.

<selenium>
  <browser name="Firefox on Linux"
           browser="*firefox /usr/lib/firefox/firefox-bin"
           host="my.linux.box"
           port="4444"
           timeout="30000"/>
</selenium>

O arquivo de configuração XML acima corresponde ao seguinte código PHP:

class WebTest extends PHPUnit_Extensions_SeleniumTestCase
{
    public static $browsers = array(
      array(
        'name'    => 'Firefox on Linux',
        'browser' => '*firefox /usr/lib/firefox/firefox-bin',
        'host'    => 'my.linux.box',
        'port'    => 4444,
        'timeout' => 30000
      )
    );

    // ...
}

Apêndice D. Atualizando

Atualizando de PHPUnit 3.7 para PHPUnit 4.0

  • O suporte limitado para esboçando e falsificando métodos estáticos que foi introduzido no PHPUnit 3.5 foi removido. Essa funcionalidade apenas funciona quando o método estático esboçado ou falsificado era invocado de um outro método da mesma classe. Acreditamos que o uso limitado desta funcionalidade não justificava o aumento da complexidade em geradores de dublês de testes em que incorreu. Pedimos desculpas por qualquer inconveniência que esta remoção possa causar e incentivamos a refatoração do código sobre teste para não exigir esse recurso para testes.

  • O addRiskyTest() foi adicionado a interface PHPUnit_Framework_TestListener. Classes que implementam esta interface tem que implementar esse novo método. Essa é a razão por quê o PHPStorm 7 não é compatível com o PHPUnit 4, por exemplo.

  • As correções para #552, #573, e #582 necessita de uma alteração de como caminhos relativos são resolvidos para o arquivo de configuração XML do PHPUnit. Todos caminhos relativos no arquivo de configuração agora são resolvidos em relação a esse arquivo de configuração. Quando atualizar, você pode precisar atualizar os caminhos relativos para as configurações testSuiteLoaderFile, printerFile, testsuites/file, e testsuites/exclude.

  • O comparador numérico não é mais invocado quando fornecido com duas strings.

Por favor, note que iniciar com PHPUnit 4.0.0, o pacote PEAR do PHPUnit é apenas um mecanismo de distribuição para o PHP Archive (PHAR) e que muitas dependências do PHPUnit não irão mais ser lançadas individualmente através do PEAR. Iremos eventualmente parar de fazer lançamentos do PHPUnit disponível através do PEAR completamente.

Por favor, note que usando o instalador PEAR para atualizar de PHPUnit 3.7 para PHPUnit 4.0 vai deixar o arquivo-fonte obsoleto de versões anteriores das dependências do PHPUnit (PHP_CodeCoverage, PHPUnit_MockObject, ...) por trás de seu diretório PEAR do ambiente do PHP. Aconselha-se desinstalar os respectivos pacotes PEAR.

Apêndice E. Índice

Símbolos

$backupGlobalsBlacklist, Estado Global
$backupStaticAttributesBlacklist, Estado Global
@author, Opções de linha-de-comando, @author
@backupGlobals, Estado Global, @backupGlobals
@backupStaticAttributes, Estado Global, @backupStaticAttributes
@codeCoverageIgnore, Ignorando Blocos de Código, @codeCoverageIgnore*
@codeCoverageIgnoreEnd, Ignorando Blocos de Código, @codeCoverageIgnore*
@codeCoverageIgnoreStart, Ignorando Blocos de Código, @codeCoverageIgnore*
@covers, Especificando métodos cobertos, @covers
@coversDefaultClass, @coversDefaultClass
@coversNothing, Especificando métodos cobertos, @coversNothing
@dataProvider, Provedores de Dados, @dataProvider
@depends, Dependências de Testes, Provedores de Dados, @depends
@expectedException, Testando Exceções, @expectedException
@expectedExceptionCode, Testando Exceções, @expectedExceptionCode
@expectedExceptionMessage, Testando Exceções, @expectedExceptionMessage
@expectedExceptionMessageRegExp, Testando Exceções, @expectedExceptionMessageRegExp
@group, Opções de linha-de-comando, @group
@large, @large
@medium, @medium
@preserveGlobalState, @preserveGlobalState
@requires, @requires
@runInSeparateProcess, @runInSeparateProcess
@runTestsInSeparateProcesses, @runTestsInSeparateProcesses
@small, @small
@test, Escrevendo Testes para o PHPUnit, @test
@testdox, @testdox
@ticket, @ticket
@uses, @uses

A

Ambientes, Ambientes
Anotação, Escrevendo Testes para o PHPUnit, Dependências de Testes, Provedores de Dados, Testando Exceções, Opções de linha-de-comando, Ignorando Blocos de Código, Especificando métodos cobertos
Anotações, Anotações
anything(), assertThat()
arrayHasKey(), assertThat()
assertArrayHasKey(), assertArrayHasKey()
assertArrayNotHasKey(), assertArrayHasKey()
assertArraySubset(), assertArraySubset()
assertAttributeContains(), assertContains()
assertAttributeContainsOnly(), assertContainsOnly()
assertAttributeEmpty(), assertEmpty()
assertAttributeEquals(), assertEquals()
assertAttributeGreaterThan(), assertGreaterThan()
assertAttributeGreaterThanOrEqual(), assertGreaterThanOrEqual()
assertAttributeInstanceOf(), assertInstanceOf()
assertAttributeInternalType(), assertInternalType()
assertAttributeLessThan(), assertLessThan()
assertAttributeLessThanOrEqual(), assertLessThanOrEqual()
assertAttributeNotContains(), assertContains()
assertAttributeNotContainsOnly(), assertContainsOnly()
assertAttributeNotEmpty(), assertEmpty()
assertAttributeNotEquals(), assertEquals()
assertAttributeNotInstanceOf(), assertInstanceOf()
assertAttributeNotInternalType(), assertInternalType()
assertAttributeNotSame(), assertSame()
assertAttributeSame(), assertSame()
assertClassHasAttribute(), assertClassHasAttribute()
assertClassHasStaticAttribute(), assertClassHasStaticAttribute()
assertClassNotHasAttribute(), assertClassHasAttribute()
assertClassNotHasStaticAttribute(), assertClassHasStaticAttribute()
assertContains(), assertContains()
assertContainsOnly(), assertContainsOnly()
assertContainsOnlyInstancesOf(), assertContainsOnlyInstancesOf()
assertCount(), assertCount()
assertEmpty(), assertEmpty()
assertEquals(), assertEquals()
assertEqualXMLStructure(), assertEqualXMLStructure()
assertFalse(), assertFalse()
assertFileEquals(), assertFileEquals()
assertFileExists(), assertFileExists()
assertFileNotEquals(), assertFileEquals()
assertFileNotExists(), assertFileExists()
assertGreaterThan(), assertGreaterThan()
assertGreaterThanOrEqual(), assertGreaterThanOrEqual()
assertInstanceOf(), assertInstanceOf()
assertInternalType(), assertInternalType()
assertJsonFileEqualsJsonFile(), assertJsonFileEqualsJsonFile()
assertJsonFileNotEqualsJsonFile(), assertJsonFileEqualsJsonFile()
assertJsonStringEqualsJsonFile(), assertJsonStringEqualsJsonFile()
assertJsonStringEqualsJsonString(), assertJsonStringEqualsJsonString()
assertJsonStringNotEqualsJsonFile(), assertJsonStringEqualsJsonFile()
assertJsonStringNotEqualsJsonString(), assertJsonStringEqualsJsonString()
assertLessThan(), assertLessThan()
assertLessThanOrEqual(), assertLessThanOrEqual()
assertNotContains(), assertContains()
assertNotContainsOnly(), assertContainsOnly()
assertNotCount(), assertCount()
assertNotEmpty(), assertEmpty()
assertNotEquals(), assertEquals()
assertNotInstanceOf(), assertInstanceOf()
assertNotInternalType(), assertInternalType()
assertNotNull(), assertNull()
assertNotRegExp(), assertRegExp()
assertNotSame(), assertSame()
assertNull(), assertNull()
assertObjectHasAttribute(), assertObjectHasAttribute()
assertObjectNotHasAttribute(), assertObjectHasAttribute()
assertPostConditions(), Ambientes
assertPreConditions(), Ambientes
assertRegExp(), assertRegExp()
assertSame(), assertSame()
assertStringEndsNotWith(), assertStringEndsWith()
assertStringEndsWith(), assertStringEndsWith()
assertStringEqualsFile(), assertStringEqualsFile()
assertStringMatchesFormat(), assertStringMatchesFormat()
assertStringMatchesFormatFile(), assertStringMatchesFormatFile()
assertStringNotEqualsFile(), assertStringEqualsFile()
assertStringNotMatchesFormat(), assertStringMatchesFormat()
assertStringNotMatchesFormatFile(), assertStringMatchesFormatFile()
assertStringStartsNotWith(), assertStringStartsWith()
assertStringStartsWith(), assertStringStartsWith()
assertThat(), assertThat()
assertTrue(), assertTrue()
assertXmlFileEqualsXmlFile(), assertXmlFileEqualsXmlFile()
assertXmlFileNotEqualsXmlFile(), assertXmlFileEqualsXmlFile()
assertXmlStringEqualsXmlFile(), assertXmlStringEqualsXmlFile()
assertXmlStringEqualsXmlString(), assertXmlStringEqualsXmlString()
assertXmlStringNotEqualsXmlFile(), assertXmlStringEqualsXmlFile()
assertXmlStringNotEqualsXmlString(), assertXmlStringEqualsXmlString()
attribute(), assertThat()
attributeEqualTo(), assertThat()

D

Dependências de Testes, Dependências de Testes
Documentação Ágil, Opções de linha-de-comando, Documentação Ágil
Documentação Automatizada, Documentação Ágil
Documentando Suposições, Documentação Ágil
Dublê de Teste, Dublês de Testes

G

getMock(), Esboços (stubs)
getMockBuilder(), Esboços (stubs)
getMockForAbstractClass(), Falsificando Traits e Classes Abstratas
getMockForTrait(), Falsificando Traits e Classes Abstratas
getMockFromWsdl(), Esboçando e Falsificando Serviços Web
greaterThan(), assertThat()
greaterThanOrEqual(), assertThat()
Grupos de Teste, Opções de linha-de-comando, Grupos
Grupos de Testes, Opções de linha-de-comando

H

hasAttribute(), assertThat()

I

identicalTo(), assertThat()
include_path, Opções de linha-de-comando
Índice de Anti-Patterns de Mudança de Risco (CRAP - Change Risk Anti-Patterns), Métricas de Software para Cobertura de Código
Interface Fluente, Esboços (stubs)
isFalse(), assertThat()
isInstanceOf(), assertThat()
isNull(), assertThat()
Isoalmento de Teste, Opções de linha-de-comando
Isolamento de Processo, Opções de linha-de-comando
Isolamento de Teste, Opções de linha-de-comando
Isolamento de Testes, Estado Global
isTrue(), assertThat()
isType(), assertThat()

M

Manipulador de Erros, Testando Erros PHP
matchesRegularExpression(), assertThat()
method(), Esboços (stubs)
Método Modelo, Ambientes

O

Objeto Falso, Objetos Falsos
onConsecutiveCalls(), Esboços (stubs)
onNotSuccessfulTest(), Ambientes
Ouvintes de Teste, Ouvintes de Teste

P

PHP Error, Testando Erros PHP
PHP Notice, Testando Erros PHP
PHP Warning, Testando Erros PHP
php.ini, Definindo configurações PHP INI, Constantes e Variáveis Globais
PHPUnit_Extensions_RepeatedTest, Subclasse PHPUnit_Extensions_TestDecorator
PHPUnit_Extensions_Selenium2TestCase, PHPUnit_Extensions_Selenium2TestCase
PHPUnit_Extensions_SeleniumTestCase, PHPUnit_Extensions_SeleniumTestCase
PHPUnit_Extensions_TestDecorator, Subclasse PHPUnit_Extensions_TestDecorator
PHPUnit_Framework_BaseTestListener, Implementando PHPUnit_Framework_TestListener
PHPUnit_Framework_Error, Testando Erros PHP
PHPUnit_Framework_Error_Notice, Testando Erros PHP
PHPUnit_Framework_Error_Warning, Testando Erros PHP
PHPUnit_Framework_IncompleteTest, Testes Incompletos
PHPUnit_Framework_IncompleteTestError, Testes Incompletos
PHPUnit_Framework_Test, Implementando PHPUnit_Framework_Test
PHPUnit_Framework_TestCase, Escrevendo Testes para o PHPUnit, Subclasse PHPUnit_Framework_TestCase
PHPUnit_Framework_TestListener, Opções de linha-de-comando, Implementando PHPUnit_Framework_TestListener, Ouvintes de Teste
PHPUnit_Runner_TestSuiteLoader, Opções de linha-de-comando
PHPUnit_Util_Printer, Opções de linha-de-comando
PHP_Invoker, @large, @medium, @small
Programação Extrema, Documentação Ágil

R

Refatorando, Durante o Desenvolvimento
Registrando, Registrando, Registrando
Relatório, Opções de linha-de-comando
returnArgument(), Esboços (stubs)
returnCallback(), Esboços (stubs)
returnSelf(), Esboços (stubs)
returnValueMap(), Esboços (stubs)

S

Selenium RC, Configurando Navegadores para Selenium RC
Servidor Selenium, Servidor Selenium
setUp(), Ambientes
setUpBeforeClass, Compartilhando Ambientes
setUpBeforeClass(), Ambientes
Sistema Sob Teste, Dublês de Testes
stringContains(), assertThat()
stringEndsWith(), assertThat()
stringStartsWith(), assertThat()
Suíte de Testes, Organizando Testes
Suítes de Teste, Suítes de Teste

T

tearDown(), Ambientes
tearDownAfterClass, Compartilhando Ambientes
tearDownAfterClass(), Ambientes
Test Isolation, Opções de linha-de-comando
TestDox, Documentação Ágil, @testdox
Testes Guiados por Dados, Implementando PHPUnit_Framework_Test
Testes Incompletos, Testes Incompletos
throwException(), Esboços (stubs)
timeoutForLargeTests, @large
timeoutForMediumTests, @medium
timeoutForSmallTests, @small

W

will(), Esboços (stubs)
willReturn(), Esboços (stubs)

Apêndice F. Bibliografia

[Astels2003] Test Driven Development. David Astels. Copyright © 2003. Prentice Hall. ISBN 0131016490.

[Beck2002] Test Driven Development by Example. Kent Beck. Copyright © 2002. Addison-Wesley. ISBN 0-321-14653-0.

[Meszaros2007] xUnit Test Patterns: Refactoring Test Code. Gerard Meszaros. Copyright © 2007. Addison-Wesley. ISBN 978-0131495050.

Apêndice G. Direitos autorais

Direitos autorais (c) 2005-2016 Sebastian Bergmann.

Este trabalho é licenciado sob a Licença Não-adaptada Creative Commons Attribution 3.0.

Um resumo da licença é dada abaixo, seguido pelo texto legal 
completo.

--------------------------------------------------------------------

Você tem o direito de:

    * Compartilhar — copiar, distribuir e transmitir o material
    * Adaptar — adaptar o material

De acordo com os termos seguintes:

Atribuição. Você deve atribuir o material da maneira especificado pelo
autor ou licenciador (mas não de qualquer form que sugira que eles
endossem você ou seu uso ao material).

    * Para qualquer reuso ou distribuição, você deve deixar claro 
      os termos da licença deste material. A melhor maneira de fazer isso e com
      um link para esta página web.

    * Qualquer das condições acima podem ser renunciadas se você 
      obter permissão de detentor dos direitos autorais.

    * Nada nesta licença prejudica ou restringe os diretos autorais do
      autor.

Sua justa negação e outros direitos não são, de maneira alguma, 
afetados pelo citado acima.

Esse é um resumo legível do Código Legal (a licença
completa) abaixo.

====================================================================
  
Creative Commons Legal Code
Attribution 3.0 Unported

CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
LEGAL SERVICES. DISTRIBUTION OF THIS LICENSE DOES NOT CREATE AN
ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO
WARRANTIES REGARDING THE INFORMATION PROVIDED, AND DISCLAIMS
LIABILITY FOR DAMAGES RESULTING FROM ITS USE.

License

THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS
CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS
PROTECTED BY COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE
WORK OTHER THAN AS AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW
IS PROHIBITED.

BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND
AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS
LICENSE MAY BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU
THE RIGHTS CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF
SUCH TERMS AND CONDITIONS.

1. Definitions

   a. "Adaptation" means a work based upon the Work, or upon the
      Work and other pre-existing works, such as a translation,
      adaptation, derivative work, arrangement of music or other
      alterations of a literary or artistic work, or phonogram or
      performance and includes cinematographic adaptations or any
      other form in which the Work may be recast, transformed, or
      adapted including in any form recognizably derived from the
      original, except that a work that constitutes a Collection
      will not be considered an Adaptation for the purpose of this
      License. For the avoidance of doubt, where the Work is a
      musical work, performance or phonogram, the synchronization of
      the Work in timed-relation with a moving image ("synching")
      will be considered an Adaptation for the purpose of this
      License.

   b. "Collection" means a collection of literary or artistic works,
      such as encyclopedias and anthologies, or performances,
      phonograms or broadcasts, or other works or subject matter
      other than works listed in Section 1(f) below, which, by
      reason of the selection and arrangement of their contents,
      constitute intellectual creations, in which the Work is
      included in its entirety in unmodified form along with one or
      more other contributions, each constituting separate and
      independent works in themselves, which together are assembled
      into a collective whole. A work that constitutes a Collection
      will not be considered an Adaptation (as defined above) for
      the purposes of this License.

   c. "Distribute" means to make available to the public the
      original and copies of the Work or Adaptation, as appropriate,
      through sale or other transfer of ownership.

   d. "Licensor" means the individual, individuals, entity or
      entities that offer(s) the Work under the terms of this License.

   e. "Original Author" means, in the case of a literary or artistic
      work, the individual, individuals, entity or entities who
      created the Work or if no individual or entity can be
      identified, the publisher; and in addition (i) in the case of
      a performance the actors, singers, musicians, dancers, and
      other persons who act, sing, deliver, declaim, play in,
      interpret or otherwise perform literary or artistic works or
      expressions of folklore; (ii) in the case of a phonogram the
      producer being the person or legal entity who first fixes the
      sounds of a performance or other sounds; and, (iii) in the
      case of broadcasts, the organization that transmits the
      broadcast.

   f. "Work" means the literary and/or artistic work offered under
      the terms of this License including without limitation any
      production in the literary, scientific and artistic domain,
      whatever may be the mode or form of its expression including
      digital form, such as a book, pamphlet and other writing; a
      lecture, address, sermon or other work of the same nature; a
      dramatic or dramatico-musical work; a choreographic work or
      entertainment in dumb show; a musical composition with or
      without words; a cinematographic work to which are assimilated
      works expressed by a process analogous to cinematography; a
      work of drawing, painting, architecture, sculpture, engraving
      or lithography; a photographic work to which are assimilated
      works expressed by a process analogous to photography; a work
      of applied art; an illustration, map, plan, sketch or three-
      dimensional work relative to geography, topography,
      architecture or science; a performance; a broadcast; a
      phonogram; a compilation of data to the extent it is protected
      as a copyrightable work; or a work performed by a variety or
      circus performer to the extent it is not otherwise considered
      a literary or artistic work.

   g. "You" means an individual or entity exercising rights under
      this License who has not previously violated the terms of
      this License with respect to the Work, or who has received
      express permission from the Licensor to exercise rights under
      this License despite a previous violation.

   h. "Publicly Perform" means to perform public recitations of the
      Work and to communicate to the public those public
      recitations, by any means or process, including by wire or
      wireless means or public digital performances; to make
      available to the public Works in such a way that members of
      the public may access these Works from a place and at a place
      individually chosen by them; to perform the Work to the public
      by any means or process and the communication to the public of
      the performances of the Work, including by public digital
      performance; to broadcast and rebroadcast the Work by any
      means including signs, sounds or images.

   i. "Reproduce" means to make copies of the Work by any means
      including without limitation by sound or visual recordings and
      the right of fixation and reproducing fixations of the Work,
      including storage of a protected performance or phonogram in
      digital form or other electronic medium.

2. Fair Dealing Rights. Nothing in this License is intended to
   reduce, limit, or restrict any uses free from copyright or rights
   arising from limitations or exceptions that are provided for in
   connection with the copyright protection under copyright law or
   other applicable laws.

3. License Grant. Subject to the terms and conditions of this
   License, Licensor hereby grants You a worldwide, royalty-free,
   non-exclusive, perpetual (for the duration of the applicable
   copyright) license to exercise the rights in the Work as stated
   below:

   a. to Reproduce the Work, to incorporate the Work into one or
      more Collections, and to Reproduce the Work as incorporated
      in the Collections;

   b. to create and Reproduce Adaptations provided that any such
      Adaptation, including any translation in any medium, takes
      reasonable steps to clearly label, demarcate or otherwise
      identify that changes were made to the original Work. For
      example, a translation could be marked "The original work was
      translated from English to Spanish," or a modification could
      indicate "The original work has been modified.";

   c. to Distribute and Publicly Perform the Work including as
      incorporated in Collections; and,

   d. to Distribute and Publicly Perform Adaptations.

   e. For the avoidance of doubt:

      i. Non-waivable Compulsory License Schemes. In those
         jurisdictions in which the right to collect royalties
         through any statutory or compulsory licensing scheme cannot
         be waived, the Licensor reserves the exclusive right to
         collect such royalties for any exercise by You of the
         rights granted under this License;

      ii. Waivable Compulsory License Schemes. In those
          jurisdictions in which the right to collect royalties
          through any statutory or compulsory licensing scheme can
          be waived, the Licensor waives the exclusive right to
          collect such royalties for any exercise by You of the
          rights granted under this License; and,

      iii. Voluntary License Schemes. The Licensor waives the right
           to collect royalties, whether individually or, in the
           event that the Licensor is a member of a collecting
           society that administers voluntary licensing schemes, via
           that society, from any exercise by You of the rights
           granted under this License.

The above rights may be exercised in all media and formats whether
now known or hereafter devised. The above rights include the right
to make such modifications as are technically necessary to exercise
the rights in other media and formats. Subject to Section 8(f), all
rights not expressly granted by Licensor are hereby reserved.

4. Restrictions. The license granted in Section 3 above is expressly
   made subject to and limited by the following restrictions:

   a. You may Distribute or Publicly Perform the Work only under the
      terms of this License. You must include a copy of, or the
      Uniform Resource Identifier (URI) for, this License with every
      copy of the Work You Distribute or Publicly Perform. You may
      not offer or impose any terms on the Work that restrict the
      terms of this License or the ability of the recipient of the
      Work to exercise the rights granted to that recipient under
      the terms of the License. You may not sublicense the Work. You
      must keep intact all notices that refer to this License and to
      the disclaimer of warranties with every copy of the Work You
      Distribute or Publicly Perform. When You Distribute or
      Publicly Perform the Work, You may not impose any effective
      technological measures on the Work that restrict the ability
      of a recipient of the Work from You to exercise the rights
      granted to that recipient under the terms of the License. This
      Section 4(a) applies to the Work as incorporated in a
      Collection, but this does not require the Collection apart
      from the Work itself to be made subject to the terms of this
      License. If You create a Collection, upon notice from any
      Licensor You must, to the extent practicable, remove from the
      Collection any credit as required by Section 4(b), as
      requested. If You create an Adaptation, upon notice from any
      Licensor You must, to the extent practicable, remove from the
      Adaptation any credit as required by Section 4(b), as requested.

   b. If You Distribute, or Publicly Perform the Work or any
      Adaptations or Collections, You must, unless a request has
      been made pursuant to Section 4(a), keep intact all copyright
      notices for the Work and provide, reasonable to the medium or
      means You are utilizing: (i) the name of the Original Author
      (or pseudonym, if applicable) if supplied, and/or if the
      Original Author and/or Licensor designate another party or
      parties (e.g., a sponsor institute, publishing entity,
      journal) for attribution ("Attribution Parties") in Licensor's
      copyright notice, terms of service or by other reasonable
      means, the name of such party or parties; (ii) the title of
      the Work if supplied; (iii) to the extent reasonably
      practicable, the URI, if any, that Licensor specifies to be
      associated with the Work, unless such URI does not refer to
      the copyright notice or licensing information for the Work;
      and (iv), consistent with Section 3(b), in the case of an
      Adaptation, a credit identifying the use of the Work in the
      Adaptation (e.g., "French translation of the Work by Original
      Author," or "Screenplay based on original Work by Original
      Author"). The credit required by this Section 4 (b) may be
      implemented in any reasonable manner; provided, however, that
      in the case of a Adaptation or Collection, at a minimum such
      credit will appear, if a credit for all contributing authors
      of the Adaptation or Collection appears, then as part of these
      credits and in a manner at least as prominent as the credits
      for the other contributing authors. For the avoidance of
      doubt, You may only use the credit required by this Section
      for the purpose of attribution in the manner set out above
      and, by exercising Your rights under this License, You may not
      implicitly or explicitly assert or imply any connection with,
      sponsorship or endorsement by the Original Author, Licensor
      and/or Attribution Parties, as appropriate, of You or Your use
      of the Work, without the separate, express prior written
      permission of the Original Author, Licensor and/or
      Attribution Parties.

   c. Except as otherwise agreed in writing by the Licensor or as
      may be otherwise permitted by applicable law, if You
      Reproduce, Distribute or Publicly Perform the Work either by
      itself or as part of any Adaptations or Collections, You must
      not distort, mutilate, modify or take other derogatory action
      in relation to the Work which would be prejudicial to the
      Original Author's honor or reputation. Licensor agrees that in
      those jurisdictions (e.g. Japan), in which any exercise of the
      right granted in Section 3(b) of this License (the right to
      make Adaptations) would be deemed to be a distortion,
      mutilation, modification or other derogatory action
      prejudicial to the Original Author's honor and reputation, the
      Licensor will waive or not assert, as appropriate, this
      Section, to the fullest extent permitted by the applicable
      national law, to enable You to reasonably exercise Your right
      under Section 3(b) of this License (right to make Adaptations)
      but not otherwise.

5. Representations, Warranties and Disclaimer

UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING,
LICENSOR OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR
WARRANTIES OF ANY KIND CONCERNING THE WORK, EXPRESS, IMPLIED,
STATUTORY OR OTHERWISE, INCLUDING, WITHOUT LIMITATION, WARRANTIES OF
TITLE, MERCHANTIBILITY, FITNESS FOR A PARTICULAR PURPOSE,
NONINFRINGEMENT, OR THE ABSENCE OF LATENT OR OTHER DEFECTS,
ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT
DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF
IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.

6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY
   APPLICABLE LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY
   LEGAL THEORY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE
   OR EXEMPLARY DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF
   THE WORK, EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY
   OF SUCH DAMAGES.

7. Termination

   a. This License and the rights granted hereunder will terminate
      automatically upon any breach by You of the terms of this
      License. Individuals or entities who have received Adaptations
      or Collections from You under this License, however, will not
      have their licenses terminated provided such individuals or
      entities remain in full compliance with those licenses.
      Sections 1, 2, 5, 6, 7, and 8 will survive any termination of
      this License.

   b. Subject to the above terms and conditions, the license granted
      here is perpetual (for the duration of the applicable
      copyright in the Work). Notwithstanding the above, Licensor
      reserves the right to release the Work under different license
      terms or to stop distributing the Work at any time; provided,
      however that any such election will not serve to withdraw this
      License (or any other license that has been, or is required to
      be, granted under the terms of this License), and this License
      will continue in full force and effect unless terminated as
      stated above.

8. Miscellaneous

   a. Each time You Distribute or Publicly Perform the Work or a
      Collection, the Licensor offers to the recipient a license to
      the Work on the same terms and conditions as the license
      granted to You under this License.

   b. Each time You Distribute or Publicly Perform an Adaptation,
      Licensor offers to the recipient a license to the original
      Work on the same terms and conditions as the license granted
      to You under this License.

   c. If any provision of this License is invalid or unenforceable
      under applicable law, it shall not affect the validity or
      enforceability of the remainder of the terms of this License,
      and without further action by the parties to this agreement,
      such provision shall be reformed to the minimum extent
      necessary to make such provision valid and enforceable.

   d. No term or provision of this License shall be deemed waived
      and no breach consented to unless such waiver or consent shall
      be in writing and signed by the party to be charged with such
      waiver or consent.

   e. This License constitutes the entire agreement between the
      parties with respect to the Work licensed here. There are no
      understandings, agreements or representations with respect to
      the Work not specified here. Licensor shall not be bound by
      any additional provisions that may appear in any communication
      from You. This License may not be modified without the mutual
      written agreement of the Licensor and You.

   f. The rights granted under, and the subject matter referenced,
      in this License were drafted utilizing the terminology of the
      Berne Convention for the Protection of Literary and Artistic
      Works (as amended on September 28, 1979), the Rome Convention
      of 1961, the WIPO Copyright Treaty of 1996, the WIPO
      Performances and Phonograms Treaty of 1996 and the Universal
      Copyright Convention (as revised on July 24, 1971). These
      rights and subject matter take effect in the relevant
      jurisdiction in which the License terms are sought to be
      enforced according to the corresponding provisions of the
      implementation of those treaty provisions in the applicable
      national law. If the standard suite of rights granted under
      applicable copyright law includes additional rights not
      granted under this License, such additional rights are deemed
      to be included in the License; this License is not intended to
      restrict the license of any rights under applicable law.

Creative Commons is not a party to this License, and makes no
warranty whatsoever in connection with the Work. Creative Commons
will not be liable to You or any party on any legal theory for any
damages whatsoever, including without limitation any general,
special, incidental or consequential damages arising in connection
to this license. Notwithstanding the foregoing two (2) sentences,
if Creative Commons has expressly identified itself as the Licensor
hereunder, it shall have all rights and obligations of Licensor.

Except for the limited purpose of indicating to the public that the
Work is licensed under the CCPL, Creative Commons does not authorize
the use by either party of the trademark "Creative Commons" or any
related trademark or logo of Creative Commons without the prior
written consent of Creative Commons. Any permitted use will be in
compliance with Creative Commons' then-current trademark usage
guidelines, as may be published on its website or otherwise made
available upon request from time to time. For the avoidance of
doubt, this trademark restriction does not form part of this
License.

Creative Commons may be contacted at http://creativecommons.org/.

====================================================================