Introdução a testes em PHP
LidoEsta aula introduz os conceitos fundamentais de testes em PHP, abordando a importância de testar, os diferentes tipos de testes (unitários, de integração, etc.), uma visão geral do PHPUnit como ferramenta padrão, e a criação do primeiro teste com exemplos práticos. O aluno aprenderá a configurar e executar testes básicos, compreendendo a estrutura de um teste e a sintaxe do PHPUnit.
Testar software é uma prática essencial no desenvolvimento moderno, e no PHP não é diferente. Escrever testes automatizados ajuda a garantir que o código funcione como esperado, facilita a manutenção e previne regressões. Nesta aula, vamos explorar os motivos para testar, os tipos de testes existentes, conhecer o PHPUnit – a principal ferramenta de testes para PHP – e escrever nosso primeiro teste. Ao final, você terá uma base sólida para começar a implementar testes em seus projetos.
Muitos desenvolvedores iniciantes negligenciam os testes por acharem que consomem tempo, mas na prática eles economizam horas de depuração e trazem confiança para refatorar e adicionar novas funcionalidades. Vamos desmistificar o processo e mostrar que testar pode ser simples e gratificante.
Por que testar
Testar não é apenas uma formalidade; é uma forma de validar que o código atende aos requisitos e continua funcionando ao longo do tempo. Quando você escreve testes, está documentando o comportamento esperado do sistema, o que auxilia novos desenvolvedores a entenderem o código. Além disso, testes automatizados permitem detectar erros imediatamente após uma alteração, evitando que bugs cheguem à produção.
Sem testes, qualquer mudança em uma parte do sistema pode quebrar funcionalidades em outros lugares sem que ninguém perceba até que um usuário final reclame. Isso gera retrabalho e aumenta o custo de manutenção. Com testes, você pode refatorar com segurança, sabendo que se algo quebrar, o teste falhará e indicará o problema. Outro ponto importante: testes incentivam um design de código mais limpo e modular, pois para testar bem, você precisa escrever funções e classes com responsabilidades bem definidas.
Benefícios principais:
- Redução de bugs: testes capturam erros antes da publicação.
- Confiança para mudanças: permite alterar código sem medo de quebrar algo.
- Documentação viva: testes mostram como a função deve ser usada.
- Economia de tempo: embora exijam investimento inicial, reduzem o tempo de depuração.
- Qualidade do código: incentiva boas práticas e separação de responsabilidades.
Tipos de teste
Existem vários níveis de teste, cada um com um objetivo específico. Os mais comuns no desenvolvimento de software são:
- Testes unitários: verificam uma unidade isolada de código, como uma função ou método. São rápidos e focados.
- Testes de integração: garantem que diferentes módulos ou serviços funcionem juntos corretamente, por exemplo, interação com banco de dados.
- Testes funcionais: validam o comportamento do sistema de ponta a ponta, simulando ações do usuário.
- Testes de aceitação: definem critérios de aceitação do cliente, geralmente automatizados com ferramentas como Behat.
- Testes de performance: avaliam a velocidade e a escalabilidade do sistema.
No contexto de PHP, os testes unitários são os mais difundidos e o PHPUnit é a ferramenta padrão para escrevê-los. Testes de integração também são comuns, especialmente quando se utiliza bancos de dados ou APIs externas. Nesta aula, focaremos nos testes unitários, pois eles formam a base de qualquer estratégia de testes.
PHPUnit (visão geral)
PHPUnit é um framework de testes para PHP, inspirado no JUnit do Java. Ele permite criar testes que verificam se o código produz os resultados esperados. É altamente configurável e se integra a ferramentas de CI/CD, como GitHub Actions ou Jenkins. A versão mais recente (PHPUnit 10 ou 11) requer PHP 8.1 ou superior e traz melhorias de performance e nova API de atributos.
Para instalar, usamos o Composer, o gerenciador de dependências do PHP. O comando típico é:
composer require --dev phpunit/phpunitApós a instalação, você pode executar os testes com vendor/bin/phpunit. O PHPUnit procura por arquivos de teste em um diretório específico (por padrão, tests) e executa métodos que começam com test ou que possuem o atributo #[Test]. Cada método de teste deve fazer uma ou mais asserções usando métodos como assertEquals, assertTrue, assertFalse, entre outros.
Uma classe de teste normalmente estende a classe PHPUnit\Framework\TestCase. Dentro dela, você escreve métodos públicos que representam os testes. O PHPUnit fornece também métodos de ciclo de vida: setUp() e tearDown() para preparar e limpar o ambiente antes e depois de cada teste.
Exemplo básico de estrutura:
use PHPUnit\Framework\TestCase;
class CalculadoraTest extends TestCase
{
public function testSoma(): void
{
$calc = new Calculadora();
$this->assertEquals(4, $calc->soma(2, 2));
}
}Primeiro teste
Vamos colocar em prática tudo o que vimos. Suponha que temos uma classe Calculadora com um método soma. Primeiro, criamos um projeto com Composer e instalamos o PHPUnit. Em seguida, criamos a classe no diretório src e o teste no diretório tests.
Passo a passo:
- Inicie um projeto:
composer inite depoiscomposer require --dev phpunit/phpunit. - Crie o arquivo
src/Calculadora.phpcom a classe. - Crie o arquivo
tests/CalculadoraTest.phpcom o teste. - Configure o
composer.jsonpara o autoload de classes (PSR-4). - Execute os testes com
vendor/bin/phpunit.
Aqui está o código completo:
<?php
// src/Calculadora.php
class Calculadora {
public function soma(float $a, float $b): float {
return $a + $b;
}
}
?>
Teste:
<?php
use PHPUnit\Framework\TestCase;
class CalculadoraTest extends TestCase {
public function testSoma(): void {
$calc = new Calculadora();
$this->assertEquals(4, $calc->soma(2, 2));
}
}
?>
Para executar, no terminal digite vendor/bin/phpunit. Se tudo estiver correto, você verá uma saída com um ponto verde e a mensagem OK (1 test, 1 assertion). Ao adicionar mais testes, o PHPUnit mostrará quantos passaram e quantos falharam, com detalhes sobre as falhas.
É importante que os testes sejam independentes e não dependam de ordem de execução. Cada teste deve validar um comportamento específico. No exemplo acima, testamos apenas a soma de números positivos. Seria interessante adicionar testes para números negativos, zeros, e até mesmo para tipos inválidos, se aplicável.
Nesta aula, você aprendeu a importância dos testes, os tipos existentes, conheceu o PHPUnit e criou seu primeiro teste. Agora, pratique com os exercícios abaixo.
Boas práticas
- Escreva testes pequenos e focados em um único comportamento.
- Use nomes descritivos nos métodos de teste, como
testSomaDeNumerosPositivos. - Mantenha os testes independentes e executáveis em qualquer ordem.
- Rode os testes com frequência, de preferência a cada alteração no código.
- Não teste apenas os caminhos felizes; inclua casos de erro e extremos.
Exercícios
- Crie uma classe
Subtracaocom um métodosubtrairque retorna a diferença entre dois números. Escreva um teste que verifica a subtração de 5 e 3, esperando 2. - Adicione um teste para a
Calculadoraque verifica a soma de números negativos: -2 + -3 = -5. - Escreva um teste que falhe de propósito (por exemplo, esperando 5 para a soma de 2 e 2) e execute-o. Observe a mensagem de falha.
- Modifique a classe
Calculadorapara que o métodosomalance uma exceção se algum argumento não for numérico. Escreva um teste que verifica se a exceção é lançada. - Utilize o método
setUp()para instanciar a calculadora uma única vez e reutilize-a nos testes de soma e subtração.
// src/Subtracao.php
class Subtracao {
public function subtrair(float $a, float $b): float {
return $a - $b;
}
}
// tests/SubtracaoTest.php
use PHPUnit\Framework\TestCase;
class SubtracaoTest extends TestCase {
public function testSubtrai(): void {
$sub = new Subtracao();
$this->assertEquals(2, $sub->subtrair(5, 3));
}
}// Adicione em CalculadoraTest
public function testSomaNegativos(): void {
$calc = new Calculadora();
$this->assertEquals(-5, $calc->soma(-2, -3));
}// Adicione em CalculadoraTest
public function testSomaFalha(): void {
$calc = new Calculadora();
$this->assertEquals(5, $calc->soma(2, 2)); // Falha: esperado 5, obtido 4
}// src/Calculadora.php modificado
class Calculadora {
public function soma($a, $b): float {
if (!is_numeric($a) || !is_numeric($b)) {
throw new InvalidArgumentException('Argumentos devem ser numéricos');
}
return $a + $b;
}
}
// tests/CalculadoraTest.php
public function testSomaComStrings(): void {
$this->expectException(InvalidArgumentException::class);
$calc = new Calculadora();
$calc->soma('a', 2);
}// tests/CalculadoraTest.php
use PHPUnit\Framework\TestCase;
class CalculadoraTest extends TestCase {
private $calc;
protected function setUp(): void {
$this->calc = new Calculadora();
}
public function testSoma(): void {
$this->assertEquals(4, $this->calc->soma(2, 2));
}
public function testSubtrai(): void {
$sub = new Subtracao(); // se disponível
$this->assertEquals(2, $sub->subtrair(5, 3));
}
}