O que é PHPUnit e Por Que Usar
PHPUnit é o framework de testes unitários mais consolidado do ecossistema PHP. Ele permite escrever testes automatizados que validam o comportamento do seu código em isolamento, identificando bugs antes que cheguem à produção. A filosofia é simples: cada teste deve verificar uma única funcionalidade, ser rápido de executar e não depender de outros testes.
Usar PHPUnit traz benefícios concretos: maior confiança ao refatorar código, documentação viva do comportamento esperado, e redução de bugs em produção. Empresas sérias exigem testes unitários como parte do padrão de qualidade. Se você quer se destacar como desenvolvedor PHP profissional, dominar testes é essencial.
Instalação e Configuração Inicial
A forma mais prática de instalar PHPUnit é via Composer. Execute:
composer require --dev phpunit/phpunit ^10
Crie um arquivo phpunit.xml na raiz do seu projeto para configurar o ambiente:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd"
bootstrap="tests/bootstrap.php"
cacheDirectory=".phpunit.cache"
colors="true">
<testsuites>
<testsuite name="Unit">
<directory>tests/Unit</directory>
</testsuite>
</testsuites>
<source>
<include>
<directory>src</directory>
</include>
</source>
</phpunit>
Crie a estrutura de pastas: src/ para o código principal e tests/Unit/ para os testes. Execute vendor/bin/phpunit para rodar os testes — é isso, você está pronto para começar.
Escrevendo Seu Primeiro Teste
Exemplo Prático: Testando uma Calculadora
Criaremos uma classe Calculadora simples e seus testes. Primeiro, a classe em src/Calculadora.php:
<?php
namespace App;
class Calculadora
{
public function somar(float $a, float $b): float
{
return $a + $b;
}
public function dividir(float $a, float $b): float
{
if ($b === 0) {
throw new \InvalidArgumentException('Divisor não pode ser zero');
}
return $a / $b;
}
}
Agora o teste em tests/Unit/CalculadoraTest.php:
<?php
namespace Tests\Unit;
use App\Calculadora;
use PHPUnit\Framework\TestCase;
class CalculadoraTest extends TestCase
{
private Calculadora $calc;
protected function setUp(): void
{
$this->calc = new Calculadora();
}
public function test_soma_dois_numeros_positivos(): void
{
$resultado = $this->calc->somar(5, 3);
$this->assertEquals(8, $resultado);
}
public function test_soma_com_numeros_negativos(): void
{
$resultado = $this->calc->somar(-5, 3);
$this->assertEquals(-2, $resultado);
}
public function test_divisao_por_zero_lanca_excecao(): void
{
$this->expectException(\InvalidArgumentException::class);
$this->calc->dividir(10, 0);
}
public function test_divisao_valida(): void
{
$resultado = $this->calc->dividir(10, 2);
$this->assertEquals(5.0, $resultado);
}
}
Execute vendor/bin/phpunit tests/Unit/CalculadoraTest.php. Todos os testes devem passar. Observe a estrutura: cada método test_* é um teste isolado. O método setUp() é executado antes de cada teste, permitindo reutilizar o objeto $this->calc.
Assertions Comuns
As asserções são o coração do teste — elas verificam se o resultado está correto:
$this->assertEquals($esperado, $atual); // Igualdade
$this->assertTrue($condicao); // Verdadeiro
$this->assertFalse($condicao); // Falso
$this->assertNull($valor); // Nulo
$this->assertContains($item, $array); // Item em array
$this->assertCount(3, $array); // Quantidade
$this->assertThrows(Exception::class, $callable); // Exceção
Testes Avançados: Mocks e Stubs
Isolando Dependências com Mocks
Código real frequentemente depende de outros objetos. Para testar isoladamente, usamos mocks — objetos falsos que simulam comportamento. Imagine uma classe UsuarioService que chama um banco de dados:
<?php
namespace App;
interface UsuarioRepositorio
{
public function buscarPorEmail(string $email): ?array;
}
class UsuarioService
{
public function __construct(private UsuarioRepositorio $repo) {}
public function autenticar(string $email, string $senha): bool
{
$usuario = $this->repo->buscarPorEmail($email);
return $usuario && password_verify($senha, $usuario['senha']);
}
}
Teste com mock em tests/Unit/UsuarioServiceTest.php:
<?php
namespace Tests\Unit;
use App\UsuarioService;
use App\UsuarioRepositorio;
use PHPUnit\Framework\TestCase;
class UsuarioServiceTest extends TestCase
{
public function test_autenticacao_com_credenciais_validas(): void
{
// Criar mock do repositório
$mockRepo = $this->createMock(UsuarioRepositorio::class);
// Simular retorno do método
$mockRepo->expects($this->once())
->method('buscarPorEmail')
->with('joao@email.com')
->willReturn([
'email' => 'joao@email.com',
'senha' => password_hash('senha123', PASSWORD_BCRYPT)
]);
$service = new UsuarioService($mockRepo);
$resultado = $service->autenticar('joao@email.com', 'senha123');
$this->assertTrue($resultado);
}
public function test_autenticacao_falha_com_email_inexistente(): void
{
$mockRepo = $this->createMock(UsuarioRepositorio::class);
$mockRepo->expects($this->once())
->method('buscarPorEmail')
->willReturn(null);
$service = new UsuarioService($mockRepo);
$resultado = $service->autenticar('inexistente@email.com', 'qualquer');
$this->assertFalse($resultado);
}
}
O mock simula o repositório sem acessar o banco de dados real. expects($this->once()) valida que o método foi chamado exatamente uma vez. willReturn() define o retorno simulado. Isso garante que seu teste é rápido, isolado e confiável.
Boas Práticas e Padrões
O Padrão AAA
Todo bom teste segue o padrão Arrange-Act-Assert (Preparar-Agir-Verificar):
public function test_desconto_aplicado_corretamente(): void
{
// Arrange: preparar dados
$preco = 100.0;
$percentualDesconto = 10;
// Act: executar a ação
$precoFinal = aplicarDesconto($preco, $percentualDesconto);
// Assert: verificar resultado
$this->assertEquals(90.0, $precoFinal);
}
Nomes Descritivos
Evite nomes genéricos como test_ok(). Use test_calcula_juros_corretamente() — o nome documenta o comportamento esperado.
Dica profissional: Uma test suite bem nomeada serve como documentação viva. Qualquer desenvolvedor deve entender o que cada teste valida apenas lendo os nomes dos métodos.
Cobertura de Testes
Verifique quantas linhas do seu código estão sendo testadas:
vendor/bin/phpunit --coverage-html coverage/
Abra coverage/index.html no navegador. Almeje 80-90% de cobertura — 100% é impraticável e nem sempre necessário.
Conclusão
PHPUnit transforma desenvolvimento PHP de um processo arriscado em uma prática profissional confiável. Comece com testes simples usando assertEquals() e assertTrue(), evoluindo para mocks quando suas dependências crescerem. Lembre-se: testes devem ser rápidos, isolados e escritos antes ou junto com o código (TDD). A consistência importa mais que a perfeição — um teste simples e confiável vale mais que uma test suite complexa e flaky. Invista nessa habilidade agora e colha benefícios por toda sua carreira.