Prev Next

Capítulo 6. 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 ???, o ambiente era simplesmente o vetor que está guardado na variável $ambiente 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 de teste 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 4.2 usamos a relação produtor-consumidor entre testes para compartilhar ambientes. Isso nem sempre é desejável, ou mesmo possível. Exemplo 6.1 mostra como podemos escrever os testes do PilhaTest 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 $pilha, 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->pilha, em vez da variável de método local $pilha com o método de asserção assertEquals().

Exemplo 6.1: Usando setUp() para criar a pilha de ambientes

<?php
class PilhaTest extends PHPUnit_Framework_TestCase
{
protected $pilha;

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

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

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

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

Os métodos-modelo setUp() e tearDown() ã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 de cada primeiro e depois do último teste da classe de casos de teste, respectivamente, serem executados.

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

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

<?php
class MetodosModeloTest 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 testUm()
{
fwrite(STDOUT, __METHOD__ . "\n");
$this->assertTrue(TRUE);
}

public function testDois()
{
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 MetodosModeloTest
PHPUnit 3.7.0 by Sebastian Bergmann.

MetodosModeloTest::setUpBeforeClass
MetodosModeloTest::setUp
MetodosModeloTest::assertPreConditions
MetodosModeloTest::testOne
MetodosModeloTest::assertPostConditions
MetodosModeloTest::tearDown
MetodosModeloTest::setUp
MetodosModeloTest::assertPreConditions
MetodosModeloTest::testTwo
MetodosModeloTest::tearDown
MetodosModeloTest::onNotSuccessfulTest
MetodosModeloTest::tearDownAfterClass


Time: 0 seconds, Memory: 5.25Mb

There was 1 failure:

1) MetodosModeloTest::testDois
Failed asserting that <boolean:false> is true.
/home/sb/MetodosModeloTest.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 ignorar o tearDown(). de modo geral. Porém, se você criar muitos objetos em seu setUp(), você pode querer remover a configuração unset() você pode querer remover a configuração tearDown() para que eles possam ser coletados como lixo. A coleta de lixo dos objetos dos casos de teste não é previsível.

Variantes

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 6.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 a desconectar do banco de dados após o último teste dos casos de teste, respectivamente.

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

<?php
class BancoDeDadosTest 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 10), 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 mudança de um teste para uma variável global 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 implementação das operações de backup e restauração para atributos estáticos de classes exige PHP 5.3 (ou superior).

A implementação das operações de backup e restauração para variáveis globais e atributos estáticos de classes utiliza serialize() e unserialize().

Objetos de algumas classes fornecidas pelo próprio PHP, como PDO por exemplo, não podem ser serializadas e a operação de backup vai quebrar quando esse tipo de objeto for guardado no vetor $GLOBALS, por exemplo.

A anotação @backupGlobals que é discutida na seção chamada @backupGlobals pode ser usada para controlar as operações de backup 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 MeuTest extends PHPUnit_Framework_TestCase
{
    protected $backupListaNegraGlobais = array('variavelGlobal');

    // ...
}

Nota

Por favor, note que definir o atributo $backupListaNegraGlobais dentro do método setUp(), por exemplo, não tem efeito.

A anotação @backupStaticAttributes que é discutida na seção chamada @backupStaticAttributespode ser usada para controlar as operações de backup e restauração para atributos estáticos. Alternativamente, você pode fornecer uma lista-negra de atributos estáticos que deverão ser excluídos das operações de backup e restauração como esta:

class MeuTest extends PHPUnit_Framework_TestCase
{
    protected $backupListaNegraAtributosEstaticos = array(
      'nomeClasse' => array('nomeAtributo')
    );

    // ...
}

Nota

Por favor, note que definir o atributo $backupStaticAttributesBlacklist dentro do método setUp() , por exemplo, não tem efeito.

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