Instalando o PHPUnit no Mac OS X sem sofrimento

# Se você não instalou o PEAR, instale! Procure os tutoriais, use o XAMPP, enfim, descubra.

# Descomente e configure se você usa Proxy!!!
# sudo pear config-set http_proxy http://USUARIO:SENHA@HOST:PORTA

sudo pear channel-discover pear.phpunit.de
sudo pear channel-discover pear.symfony-project.com
sudo pear channel-discover components.ez.no
sudo pear update-channels
sudo pear upgrade-all

# E finalmente o PHPUnit
sudo pear install --alldeps phpunit/PHPUnit

# Não achou o executável?
pear config-show

# Na saída
do comando acima, procure por "PEAR executables directory"
# O executável phpunit deverá estar aí... se não estiver, use o comando abaixo:
sudo pear install --alldeps --force phpunit/PHPUnit

Instalando o PHPUnit no Ubuntu sem sofrimento


# Se você já instalou o phpunit via apt-get, remova!
sudo apt-get remove phpunit

# Se você não instalou o PEAR
sudo apt-get install pear

# Descomente e configure se você usa Proxy!!!
# sudo pear config-set http_proxy http://USUARIO:SENHA@HOST:PORTA

sudo pear channel-discover pear.phpunit.de
sudo pear channel-discover pear.symfony-project.com
sudo pear channel-discover components.ez.no
sudo pear update-channels
sudo pear upgrade-all

# E finalmente o PHPUnit
sudo pear install --alldeps phpunit/PHPUnit
sudo apt-get install phpunit 

Uma Introdução à Arte dos Testes Unitários em PHP

[Este artigo é uma tradução livre e amadora de Alan Willms do texto “An Introduction to the Art of Unit Testing in PHP”, Zend Developer Zone.]

Introdução

Teste é um aspecto essencial do desenvolvimento em qualquer linguagem de programação. Se você não testa o seu código fonte, então como você pode verificar se ele está funcionando conforme o esperado? Os testes manuais só podem ser realizados de forma irregular e, geralmente, apenas de forma limitada. A resposta para testar o código fonte regularmente, e em profundidade, é escrever testes automatizados que podem ser executadas com frequência. No PHP, tais testes geralmente são escritos usando um framework de testes unitários, um framework que permite que o código fonte de qualquer aplicativo ou biblioteca seja testado como unidades isoladas de funcionalidades, tais como uma única classe ou método. Já que os testes unitários ganharam popularidade, tornou-se uma prática comum em PHP, com bibliotecas e frameworks como o SwiftMailer, o Zend Framework e o Symfony, todos exigindo cobertura de testes unitários de seu código fonte.

Os testes unitários muitas vezes são vistos como uma tarefa misteriosa e demorada - o que às vezes pode ser! Mas o objetivo de gastar tempo escrevendo testes é: melhorar a qualidade de seu código-fonte para que ele tenha menos bugs em geral, muitos dos quais são detectados precocemente; um processo de teste contínuo para evitar que novas alterações mudem o comportamento do código antigo; e fornecer a confiança de que se pode depender do seu código. Há também outros benefícios, e nós vamos detalhá-los mais adiante.

As Falácias dos Testes

Os Testes Unitários, e na verdade todas as outras formas de testes, colidem com quatro desculpas comuns que dificultam a adoção pelos desenvolvedores.

  1. É demorado e leva muito tempo.
  2. Código complexo não pode ser testado.
  3. Desde que funcione, eu não preciso escrever testes.
  4. Testar é chato.

Estas são as falácias dos testes, desculpas que parecem bastante razoáveis, mas que na verdade são sutilmente errôneas. Então vamos esclarecer algumas coisas!

Testes consomem tempo. A questão é por que esse se deve considerar que esse tempo vale a pena, e a resposta é que ele reduz o tempo futuro que você iria gastar na modificação do código, manutenção, refatoração e correção de bugs não-detectados. E ambos sabemos que haveria toneladas de bugs não-detectados se você não estivesse testando de forma abrangente!

Testar previamente é como diz o provérbio “o passarinho que chega primeiro come a minhoca” [OK, isso não soa tão bem aqui no Brasil…]; conforme você escreve código, você pode usar Testes Unitários para testar imediatamente métodos/classes isolados ou grupos de classes funcionais. Ao fazer isso você encontra e corrige bugs tão rapidamente quanto eles são criados. Um problema é que você encontra erros tantas vezes, e corrige-os tão rapidamente, que mal percebe o tempo que levou. Quando você faz alterações posteriores, e um teste falha, você pode corrigir o problema de integração tão rapidamente quanto altera - mais tempo economizado que você mal percebe. Os benefícios podem ser tão bem camuflados que você pode perdê-los de vista por completo - e só ver o código de teste que demorou algumas horas para escrever, e não os erros que você resolveu em 10 segundos, que teriam levado minutos ou horas seis meses depois.

Em segundo lugar, existe código complexo - e existe código complexo composto de partes menores e práticas. Na OOP, um objetivo simples muitas vezes é “ser testável”. É como um medidor de código desacoplado de qualidade. Se o código não pode ser facilmente testado então não é o Teste Unitário que falhou, é você que não conseguiu escrever um código prático que fosse flexível e desacoplado. Se você estivesse testando conforme escrevesse o código, você teria sido forçado a desacoplar classes quase que automaticamente. O fato de que o código parece muito complexo para se testar, normalmente é um sintoma de se ter esperado muito tempo para começar a testar!

Em terceiro lugar, código que funciona e código testado que funciona são dois bichos diferentes. Os testes oferecem uma rede de segurança que torna as alterações, a refatoração e as novas funcionalidades menos sofríveis, já que os problemas de integração serão detectados quase que imediatamente. Eles também melhoram a eficiência dos novos programadores em sua equipe que, não familiarizados com o código, vêem seus erros detectados imediatamente pelo curto ciclo de feedback, e assim adquirem experiência sobre a execução.

Em ambos os casos, se o novo código alterar o comportamento do código antigo, os testes farão um escândalo! Sem testes, algum comportamento pode ser sutilmente alterado sem que ninguém perceba (no final das contas, se você não está testando, como você o detectaria antes de que algum usuário final no futuro percebesse e reclamasse?). Não se anime porque algo funciona sem testes - espere sua vez no bug tracker público e aguarde o ataque previsível. Se você for muito sortudo, o código será simples o suficiente de modo que, eventualmente, os relatórios de bugs descobrirão a maioria dos problemas, e você pode corrigi-los antes que alguém se irrite. Isso continua fazendo pouco por sua reputação, assim como o efeito futuro de mudanças novamente não-testadas.

Por fim, os testes nem sempre são chatos. Ah, eu sei que isso às vezes pode ser chato, mas geralmente é chato porque você está escrevendo testes no final do processo de desenvolvimento. Se você está constantemente alternando dos testes para o código, e de volta para os testes, você vai conseguir fazer mais bocejando menos. Então, a cura para o tédio é a simples alternância de tarefas e testes escritos na hora certa - não caia na armadilha de ter de passar dias escrevendo testes e apenas testes.

Portanto, não há mais desculpas. Sente, relaxe, leia.

Testes Unitários em PHP

Testes Unitários em PHP podem ser feitos usando um de três métodos. As duas opções mais comuns são o SimpleTest de Marcus Baker e o PHPUnit de Sebastian Bergmann. Para causar um pouco de confusão, também há o velho e confiável PHPT. Todos os três permitem que você faça testes unitários em seu código, e os dois frameworks também oferecem inúmeras extensões.

Vamos tomar um exemplo com o PHPUnit. Para instalar o PHPUnit, siga as instruções de instalação disponíveis online em http://www.phpunit.de/manual/3.6/en/installation.html que formam parte do Manual do PHPUnit. Você precisará de uma instalação do PEAR funcionando.

O PHPUnit (de forma muito parecida com o SimpleTest) organiza os testes em casos; basicamente uma classe cujos métodos públicos são testes singulares independentes. Aqui estamos prestes a criar uma classe para representar uma frota [Fleet] de navios [Ships] (um Modelo simples). Parece simples demais, mas vamos por partes. Aqui segue um caso de teste que eu escrevi para levantar essa classe até um nível mínimo antes de sequer pensar em um banco de dados de entrada.

<?php
require_once 'PHPUnit/Framework.php';
require_once 'My/Fleet.php';
 
class MyFleetTest extends PHPUnit_Framework_TestCase
{
    protected $_fleet = null;

    public function setUp()
    {
	$this->_fleet = new My_Fleet;
    }

    public function tearDown()
    {
        unset($this->_fleet);
    }

    public function testShouldNotHaveAnyShipsYetInIntitialState()
    {
	/**
	 * $this->_fleet->count() is boring; let's implement SPL's Countable interface
	 */
	$this->assertEquals(0, count($this->_fleet));
    }

    public function testAddingAShipWillIncrementCountByOne()
    {
	$this->_fleet->addShip('USS Enterprise');
	$this->assertEquals(1, count($this->_fleet));
    }

    public function testAfterAddingAShipWeCanRetrieveItsNameByIndex()
    {
	$this->_fleet->addShip('USS Enterprise');
	$this->assertEquals('USS Enterprise', $this->_fleet->getShip( count($this->_fleet) - 1 ));
    }
}

O caso de teste acima é um pequeno e bom começo. Nós definimos dois métodos, setUp() e tearDown(). Tanto no SimpleTest quanto no PHPUnit, estes são usados ​​para criar Fixtures. Uma Fixture é algum recurso que todos os testes em um caso de teste têm em comum, e que define o contexto do teste (a situação em que estamos testando), bem como outros objetos ou recursos repetitivos. Já que cada teste deve ser isolado (não pode compartilhar informações), estes métodos dirão ao Framework para criar nossa Fixture (uma instância de My_Fleet) antes de cada teste, e destruí-la depois. Desta forma, todo teste obtém uma versão novinha em folha para lidar, livre de qualquer coisa que um teste anterior tenha feito.

Cada teste acima é um método público cujo nome começa com “test”. Qualquer coisa que não começar com “test” não será reportada (a menos que seja indicada como um teste com o PHPUnit). Você pode usar isso para criar métodos auxiliares para a definição de um teste sem ter medo de que serão interpretados pelo framework como um teste separado. Métodos auxiliares são muito úteis para tarefas repetitivas - sim, até mesmo os testes podem ser refatorados. O próprio nome da classe termina, por convenção, com “Test”.

Dentro de cada teste, temos referências a métodos “asset”. Uma asserção [assertion] é, basicamente, pegar um valor esperado e compará-lo com um valor real. Se o valor esperado não corresponde ao valor real, então o teste falhará. Saber o que afirmar [assert] em nossos testes é praticamente 90% da batalha ganha. Os outros 10% são garantir que cada teste está totalmente isolado de qualquer outro teste (usar Fixtures e Mock Objects são os principais instrumentos para isso). Eu só usei assertEquals() acima, mas uma lista completa do PHPUnit está disponível aqui.

Não apresentaremos Mock Objects aqui, mas eles são um componente extremamente importante na manutenção do isolamento de um teste unitário de todas as classes e outros recursos, exceto o que está em teste. Vou indicá-lo a duas fontes específicas do PHP: a documentação do SimpleTest para Mock Objects, Partial Mocks e Stubs, e a página do PHPUnit Pocket Guide sobre a sua nova funcionalidade de Mock Objects.

Rodando os Testes

A execução dos testes do PHPUnit pode ser feita a partir do console (até mesmo do MS-DOS no Windows Vista) ou de um web browser. Para manter isso simples, vamos contar com um comando rápido do terminal, executado a partir de onde nós armazenamos esta classe de teste. O PHPUnit também descreve um método de organização de testes em suites, que não é coberto aqui, mas é absolutamente essencial a menos que pretenda executar todos os testes um por um por toda a eternidade, e o Manual do PHPUnit é um bom lugar para estudar sobre as Test Suites e outros elementos dos Testes Unitários.

Para executar a nossa classe de teste simples, abra seu console e navegue para onde o teste está armazenado. O comando é simples:

phpunit MyFleetTest

Assume-se que o nome do teste é refletido no nome do arquivo salvo, então a classe MyFleetTest seria armazenada no arquivo MyFleetTest.php. Execute o teste agora. Eu já aviso que ele vai falhar miseravelmente, uma vez que ainda não chegamos até o ponto de mostrar o código da classe! Mas aqui entram…

Testes Unitários e Desenvolvimento Orientado a Testes [TDD / Test-Driven Development]

Você já deve ter notado este pequeno problema. Nós escrevemos alguns testes, mas onde a classe de verdade está sendo testada? É seguro dizer que poderíamos ter escrito a classe e testado-a depois (a prática quase que comum em PHP), mas para adicionar uma perspectiva mais ampla, eu não fiz isso.

Um uso popular dos Teste Unitários é a prática do Desenvolvimento Orientado a Testes [Test-Driven Development] (TDD). TDD não é um componente indispensável de Testes Unitários, na verdade é uma prática complementar que utiliza Testes Unitários, já que é um padrão disponível para escrever exemplos executáveis. A ideia por trás do TDD é que ao descrever o comportamento de uma classe em código executável (ou seja, testes) antes de programar, isso fornece uma ferramenta para a condução de como a classe deveria ser projetada. Isto deu origem ao grito de guerra “Test First”. E também a uma população de “Test Infected”s. Um dia eles deveriam fazer um filme de terror.

Considere os testes que acabamos de escrever. Ao escrever os testes antes de escrevermos a classe, fomos forçados a fazer algumas coisas. Primeiramente, tivemos de inventar a API pública da classe antes de mais nada. Vale a pena perceber que nos Testes Unitários e TDD é praticamente inútil testar métodos privados (o que é chamado de “Teste de Estado”), já que raramente estamos interessados ​​em como o código faz alguma coisa, ao contrário do que o resultado final esperado deve ser (foco nos resultados, não no estado intermediário). Em um monte de código o resultado permanecerá o mesmo, mas o código funcional evoluirá ao longo do tempo devido à refatoração, funcionalidades do novo PHP, ou requisitos de sistemas dependentes - assim testar tudo isso só nos obriga a reescrever constantemente os nossos testes, sem nenhum bom motivo.

Agora, definindo a API pública tão cedo, nós nos encontramos projetando. Nós não estamos realmente testando em si (não há nenhum código ainda!), em vez disso estamos especificando as nossas expectativas de como a classe AGIRÁ, e, em seguida, escrevendo o código para que ela AJA conforme o especificado.

Assim que começarmos a codificação de verdade, já temos pronto todo o trabalho do design, e um conjunto de testes que irão verificar continuamente o novo código que escrevemos. Assim, enquanto TDD é uma metodologia de projeto, também acaba fornecendo um bom conjunto de testes. Então vamos escrever a nossa classe.

<?php

class My_Fleet implements Countable
{

    protected $_ships = array();

    public function addShip($shipName)
    {
	$this->_ships[] = $shipName;
    }

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

    public function getShip($index)
    {
	return $this->_ships[ intval($index) ];
    }

}

Uma classe que funciona, e todos os nossos testes passarão! Talvez mais tarde nós mudemos os testes para permitir que objetos Ship que implementam __toString() imprimam o nome do navio. Uma pequena mudança para os testes fazerem um cast strval() no valor de retorno de getShip() e nossos testes estariam atualizados para este comportamento alterado.

Alguns Benefícios dos Testes Unitários

Nós discutimos brevemente um monte de teorias e alguns exemplos, por isso espero que você esteja quase convencido de experimentar SimpleTest ou PHPUnit. Para incentivá-lo ainda mais, segue aqui uma rápida olhada sobre os benefícios que os Testes Unitários podem fornecer para suas práticas de desenvolvimento.

1. Testes Unitários reduzem o nível de erros no código de produção.

Frequentemente se você executar a sua crescente suite de testes, você detectará bugs. É algo inevitável. Detectando erros precocemente, muitas vezes você pode resolvê-los em menos tempo, uma vez que o código fonte ainda está fresco em sua memória. Além disso, para novos bugs que seus testes não encontram imediatamente, você pode escrever novos testes (Testes de Regressão [Regression Testing]) para detectar os bugs em versões futuras. Nunca deixe um bug antigo ocorrer de novo!

2. Testes Unitários economizam-lhe tempo de desenvolvimento.

Se você detectar e resolver os bugs mais perto do momento em que são introduzidos, é muito mais rápido do que ter que localizar, depurar e resolver um problema no futuro. Some a isso o tempo economizado de fazer mudanças que quebram outras funções e o constante impulso progressivo, e você gasta menos tempo em um projeto. Isso não significa necessariamente que você vai sempre terminar um projeto mais rápido, mas definitivamente significa que você gastará muito menos tempo fazendo manutenção dele durante sua vida útil. A manutenção é sempre um daqueles trabalhos custosos que nós todos adoraríamos limitar.

3. Testes automatizados podem ser executados tão frequentemente quanto for necessário.

Testes manuais são uma droga. Com testes automatizados, você pode executá-los todos com um rápido comando no terminal, ou atualização do navegador. Você pode sempre verificar se o seu código fonte está funcionando conforme o esperado, e encontrar os bugs quase no momento em que são introduzidos no código-fonte. Escrever testes é um custo único de um nível contínuo de confiança.

4. Testes Unitários facilitam a mudança e a refatoração de código.

Todos sabemos que alterar código porta um risco. Será que o novo código funcionará da mesma forma que o antigo? Adicionarei novos bugs ou erros lógicos por acidente? Será que acabarei quebrando a compatibilidade com as APIs públicas ou versões do PHP anteriores? Um conjunto de testes pode verificar todas essas questões a qualquer momento. Uma vez que os testes são sempre feitos em cima da API pública das classes, e se concentram apenas em resultados, e não no estado, você pode refatorar, alterar e adaptar o código, confiando nos seus testes para saber quando isso vai mal. Um sistema de integração contínua também pode ser configurado para testar sobre diversas versões do PHP e sistemas operacionais.

5. Testes Unitários podem melhorar o design do código, especialmente com Desenvolvimento Orientado a Testes.

Conforme já vimos, escrever exemplos do que nosso futuro código deve fazer nos ajudar a revelar como a nossa classe será projetada. Uma vez que você inclua Mock Objects isto pode até mesmo conduzir o projeto de várias classes e suas interações umas com as outras. Mesmo sem TDD, escrever testes conforme você está programando ainda melhorará o projeto, porque classes testáveis ​​devem necessariamente ser mais flexíveis e dissociadas.

6. Testes Unitários são uma forma de documentação.

Pense sobre o que um teste faz. Ele descreve um caso de uso ou exemplo. Um exemplo vale muito mais do que mil palavras de um manual para um desenvolvedor. Muitos frameworks de Testes Unitários também oferecem uma ferramenta de documento de sumarização chamada “testdox”, que basicamente lista (com base nos nomes dos métodos de teste) o que cada exemplo afirma. Em essência fornecendo um resumo das especificações da classe, que é mais legível para não-programadores (como o seu DBA!).

7. Testes Unitários te obrigam a enfrentar o problema de frente.

Não há mais evasão. Um erro frequentemente encontrado é que um desenvolvedor tenta resolver um problema escrevendo código sem estrutura ou planejamento. É como tatear o seu caminho através de um quarto escuro - você erra, precisa voltar e desviar, e perde um monte de tempo para chegar até a saída! Ao escrever testes você não tem escolha senão enfrentar como um problema pode ser resolvido através do foco na API pública e escrevendo testes que fazem sentido. Além disso, incentiva soluções mais simples e elegantes, que são práticas. Sobretudo, o impulso é mantido constante - o retrocesso se torna uma raridade.

8. Testes Unitários inspiram confiança!

Se você considerar o que os testes, e Testes Unitários, oferecem, não deveria ser surpreendente que eles acrescentam confiança. Quando você pode verificar o seu código fonte, detectar erros atuais e futuros com mais facilidade, e a mudança não é mais um risco, você gasta menos tempo corrigindo e mais tempo desenvolvendo recursos. Você está sempre olhando para frente. Leve em conta que muitos desenvolvedores mostrarão preferência por uma biblioteca, framework ou aplicativo bem testado, e este é um benefício muito valioso.

9. Testes Unitários são uma medida de compleição.

Se você escrever seus testes para cobrir todas as características e funcionalidades necessárias das classes, e todos estão passando, então é razoavelmente seguro assumir que você terminou o código. Um dia, em breve, alguém pode pedir um novo recurso, mas até esse dia chegar, você deveria parar de programar. Em certo sentido isso se mescla com as práticas de Agile/XP development, onde os testes não só podem dirigir o projeto, como também capturar os requisitos funcionais.

10. Testes Unitários são DIVERTIDOS!

Ok, existem mais benefícios que eu poderia mencionar, mas isso não é um livro sobre Testes Unitários. Mas se divertir é definitivamente algo que vale a pena mencionar. Eu me diverti muito desde que comecei com os Testes Unitários, e embora seja difícil apontar para qualquer motivo, geralmente todos os outros benefícios contribuem para isso. Quando você tem confiança no seu código, a mudança é barata, os problemas são resolvidos de uma forma mais estruturada e elegante, a refatoração é possível, e você pode olhar o código terminado com orgulho, você vai apreciar o processo. O outro jeito logo parecerá sombrio e ameaçador.

Conclusão

Já terminamos aqui a nossa rápida jornada. Espero que essa introdução aos Testes Unitários coloque os seus pés na estrada para testar o seu próximo projeto. Os testes são um passo tão essencial na programação de hoje em dia, que é muito difícil de evitá-los. Então, se você de repente se sentir encorajado e entusiasmado, leve o tempo que precisar e lembre-se da curva de aprendizado. Testadores experientes não nascem da noite pro dia, então aproveite o seu fórum de ajuda preferido, onde você é obrigado a encontrar alguns dos “Test-Infected”s à espreita para atacar os seus problemas e lhe dar uma assistência de boas-vindas.

Troll Library