Escrevendo testes de unidade com PHPUnit, parte 3: configuração XML
Nos posts anteriores desta série, abordei os dois tópicos a seguir:
- Escrevendo Testes Unitários com PHPUnit, Parte 1: A Configuração. Um guia para começar a escrever testes PHPUnit através do uso de um cache básico e usando o método setUp do framework.
- Escrevendo Testes Unitários com PHPUnit, Parte 2: The Tear Down. Um tutorial sobre como escrever testes de unidade que aproveitam adequadamente os métodos setUp e tearDown do PHPUnit.
Cada um dos itens acima destina-se a fornecer uma cartilha sobre como começar a escrever testes de unidade muito básicos. As coisas podem ficar mais complexas, especialmente à medida que um aplicativo ou projeto cresce (mas isso é sempre verdade, certo?).
Mas para ter certeza de que está preparado para isso, há um componente final para teste de unidade que acredito que devemos focar e que é entender o arquivo de configuração XML PHPUnit (que você pode ter visto em outros projetos como phpunit.xml).
Configuração XML do PHPUnit
Então, neste post, vou configurar um projeto simples que usa PHPUnit, escreve alguns testes como os que já vimos e aproveita um arquivo de configuração para automatizar os testes.
Além disso, farei o que puder para explicar melhor as partes necessárias do arquivo de configuração para que você possa incluir uma em seu próximo projeto.
1 Apagando os arquivos
Antes de começar a escrever código testável, é importante conhecer os arquivos que serão necessários para que o processo funcione.
O seguinte é, mais ou menos, como organizamos as coisas desde o início de um projeto:
- um diretório para testes,
- o arquivo phpunit.xml
Eventualmente, você também verá:
- os arquivos que compõem o projeto,
- os testes que verificam esses arquivos.
Neste ponto, porém, vamos dar uma olhada no arquivo de configuração XML e tentar executar o PHPUnit automaticamente sem quaisquer outros parâmetros.
2 Noções básicas para o arquivo de configuração
Primeiro, vamos ver o arquivo de configuração básica:
<?xml version="1.0" encoding="UTF-8"?>
<!-- http://phpunit.de/manual/4.1/en/appendixes.configuration.html -->
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://schema.phpunit.de/4.1/phpunit.xsd"
bootstrap="./tests/bootstrap.php"
backupGlobals="false"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
>
<testsuites>
<testsuite name="AcmeTests">
<directory>./tests</directory>
</testsuite>
</testsuites>
<logging>
<log type="coverage-text" target="php://stdout" showUncoveredFiles="true"></log>
</logging>
</phpunit>
Agora vamos entender exatamente o que estamos vendo (além do XML simples).
- phpunit. O nó pai faz o trabalho normal de definir o esquema para o arquivo XML, mas há alguns outros componentes com os quais estamos preocupados:
- backupGlobals. Na verdade, isso está relacionado a uma anotação que podemos fazer em nosso código-fonte. Os globais são algo que devemos tentar evitar na programação orientada a objetos, mas se você optar por usar um ou precisar usar um, isso dirá ao PHPUnit para lidar com os valores que as variáveis globais estão mantendo (e lhe dará a opção de restaurar eles). Eu geralmente deixo isso como está.
- bootstrap. Isso é opcional, mas se você optar por incluir outros arquivos com seus testes (como trazer uma biblioteca de simulação, parte do WordPress ou uma biblioteca de terceiros), isso será tudo para você definir a localização do script que precisa executar. Zombar e trazer o WordPress está fora do escopo deste post, mas é algo que provavelmente veremos no futuro, pois é útil ao testar plugins. Por enquanto, incluirei um autoloader simples que basicamente adiciona todos os arquivos na raiz do diretório do projeto. A fonte completa para ele será compartilhada mais tarde neste post.
- cores. Se você quiser que o console imprima um relatório de seus testes e faça isso usando cores (para ajudar a identificar mais facilmente avisos, avisos, erros e assim por diante), defina como true.
- A seguir estão todos os valores booleanos. Eu recomendo defini-los como verdadeiros para os relatórios mais agressivos possíveis. Dessa forma, você não escapará simplesmente de receber avisos ou avisos enquanto se preocupa apenas com erros. Este é mais um exercício de qualidade de código do que qualquer outra coisa.
- convertErrorsToExceptions
- convertNoticesToExceptions
- convertWarningsToExceptions
- testsuites são compostos de coleções de testes. Como um determinado projeto pode ter vários testes, é importante garantir que você dê a cada conjunto um nome exclusivo e faça referência ao caminho adequado para o grupo de testes. Para nosso exemplo, teremos apenas um único conjunto de testes e está localizado no diretório de testes.
- logging é um recurso que pode ser tão simples quanto imprimir dados no console ou usar uma biblioteca de terceiros (como Clover) para gerar relatórios que ajudam na integração contínua. Como eu ainda tenho que discutir o último em qualquer um dos meus posts anteriores, vamos ficar com o console como nosso principal método de saída. Assim, temos php ://stdout como nossa única saída de log.
Com tudo isso dito, nosso arquivo XML tem tudo que o PHPUnit precisa para rodar sem outros parâmetros.
Lembre-se, porém, antes de prosseguir com o resto deste artigo, suponho que você instalou globalmente o PHPUnit em seu sistema usando o Composer. Caso contrário, revise este artigo, pois ele fornecerá instruções sobre como fazê-lo.
Uma vez feito, você pode verificar se o PHPUnit está instalado digitando o seguinte comando no seu terminal:
$ which phpunit
E você deve ver algo como o seguinte:
Se você vir algo como o acima, poderá executar o PHPUnit em qualquer lugar do seu sistema.
3 O arquivo Bootstrap
Antes de prosseguir, vamos escrever um arquivo bootstrap básico. Vamos chamá-lo de bootstrap.php e colocá-lo em nosso diretório de testes. Incluirá o seguinte:
<?php
// This array has a single file but could whole the contents of an entire directory.
$files = [
dirname(__DIR__).'/AcmeCache.php',
];
foreach ($files as $file) {
if (file_exists($file)) {
require_once $file;
}
}
Este é um simples “autoloader" (que eu o chamo hesitantemente, já que está apenas iterando os arquivos e exigindo-os, mas funciona para nossos propósitos).
Neste ponto, vamos configurar um teste básico.
4 Um Teste Básico e Reprovado
Se você ler algo sobre desenvolvimento orientado a testes, provavelmente ouvirá falar sobre o ciclo de repetição vermelho-verde. Há muito a ser dito sobre isso e eu recomendo ler sobre isso, mas não é o objetivo deste post.
Em vez disso, estamos mais focados em realmente escrever testes que correspondam ao que precisamos fazer, certo? Dito isso, vamos fazer o seguinte:
- crie um diretório a partir do qual você terá alguns arquivos PHP básicos que testaremos,
- na raiz do diretório, crie também phpunit.xml e preencha-o usando o código compartilhado anteriormente neste post
- crie um diretório de testes onde colocaremos nossos testes.
Agora, a partir do Terminal, mude o diretório para o diretório do projeto (que está faltando, por enquanto) e então simplesmente execute php unit:
$ phpunit
Supondo que tudo esteja configurado corretamente, você deverá ver algo assim:
Como não temos código nem testes, naturalmente veremos a saída acima, certo? Então, vamos escrever um único teste que será executado (e falhará), pois não há código para ele realmente testar.
Primeiro, no diretório de testes, crie um arquivo chamado AcmeCacheTest.php. E vamos fazer algo simples como instanciar um objeto de cache que eventualmente criaremos.
<?php
namespace AcmeTests;
use PHPUnitFrameworkTestCase;
use AcmeAcmeCache;
class AcmeCacheTest extends TestCase
{
private $cache;
public function setUp()
{
$this->cache = new AcmeCache();
}
public function testCacheExists()
{
$this->assertNotNull($this->cache);
}
}
Antes de executar o teste, observe que:
- Certifique-se de usar PHPUnitFrameworkTestCase
- E faça nossa classe estender TestCase
Isso é parte do que torna o uso do PHPUnit tão fácil. Feito isso, execute o seguinte código na raiz do seu projeto:
$ phpunit
Depois disso, você deve ver o seguinte:
Observe que isso resultará em um teste com falha e informará onde o problema foi encontrado, o arquivo e a linha.
Para corrigir isso, precisamos escrever uma classe:
<?php
namespace Acme;
class AcmeCache
{
private $duration;
public function __construct()
{
$this->duration = 43200;
}
public function setDuration(int $duration)
{
$this->duration = $duration;
}
public function getDuration(): int
{
return $this->duration;
}
}
4 Alguns testes básicos e aprovados
O teste básico de aprovação (que será baseado no código anterior) incluirá o seguinte:
- um arquivo com namespace,
- representará um cache simples,
- será carregado automaticamente pelo PHPUnit usando o arquivo bootstrap.php compartilhado acima
- e terá uma duração definida em seu construtor junto com um setter e um getter para o valor
Primeiro, vamos testar se conseguimos configurar a classe e se ela não é nula. Esta é uma afirmação um pouco desnecessária, pois sabemos que teremos uma classe devidamente instanciada, mas nos coloca no ritmo de escrever testes:
<?php
namespace AcmeTests;
use PHPUnitFrameworkTestCase;
use AcmeAcmeCache;
class AcmeCacheTest extends TestCase
{
private $cache;
public function setUp()
{
$this->cache = new AcmeCache();
}
public function testCacheExists()
{
$this->assertNotNull($this->cache);
}
}
E execute o teste:
Em seguida, vamos verificar se o valor padrão do cache está definido:
<?php
namespace AcmeTests;
use PHPUnitFrameworkTestCase;
use AcmeAcmeCache;
class AcmeCacheTest extends TestCase
{
private $cache;
public function setUp()
{
$this->cache = new AcmeCache();
}
public function testCacheExists()
{
$this->assertNotNull($this->cache);
}
public function testDefaultCacheValue()
{
$this->assertSame(43200, $this->cache->getDuration());
}
}
Assim como na etapa anterior, execute os testes e agora você verá dois testes aprovados:
Por fim, vamos testar para ver se conseguimos alterar o valor com sucesso:
<?php
namespace AcmeTests;
use PHPUnitFrameworkTestCase;
use AcmeAcmeCache;
class AcmeCacheTest extends TestCase
{
private $cache;
public function setUp()
{
$this->cache = new AcmeCache();
}
public function testCacheExists()
{
$this->assertNotNull($this->cache);
}
public function testDefaultCacheValue()
{
$this->assertSame(43200, $this->cache->getDuration());
}
public function testSetCustomDuration()
{
$duration = 4200;
$this->cache->setDuration($duration);
$this->assertSame($duration, $this->cache->getDuration());
}
}
E os três últimos testes de aprovação:
E aí está:
- um arquivo XML PHPUnit,
- um simples bootstrap,
- uma única classe com namespace,
- testes unitários para cada método da classe
Concedido, é simples, mas isso estabelece os fundamentos para muito mais do que muitas pessoas já fazem com seus testes.
Além disso, oferece algo para você construir à medida que suas habilidades de teste se fortalecem.
Há mais? (Sempre certo?)
Finalmente, se você estiver ansioso para realmente mergulhar no arquivo de configuração, poderá ler a explicação detalhada do manual sobre ele.
Observe, no entanto, que tudo o que está descrito acima visa ser o que você precisa para começar a configurar seu próprio arquivo de configuração XML PHPUnit.




