Testes com PHPUnit: fundamentos
1. Introdução ao PHPUnit e configuração do ambiente
PHPUnit é o framework de testes unitários mais utilizado no ecossistema PHP. Criado por Sebastian Bergmann, ele permite que desenvolvedores escrevam testes automatizados para verificar se pequenas unidades de código (métodos e funções) funcionam conforme o esperado. Testar seu código PHP traz benefícios como detecção precoce de bugs, segurança para refatorações e documentação viva do comportamento esperado do sistema.
Para instalar o PHPUnit, utilize o Composer, o gerenciador de dependências do PHP:
composer require --dev phpunit/phpunit
Após a instalação, organize seu projeto com a seguinte estrutura de diretórios:
meu-projeto/
├── src/
│ └── Calculadora.php
├── tests/
│ └── CalculadoraTest.php
├── vendor/
├── composer.json
└── phpunit.xml
No arquivo composer.json, configure o autoloading para mapear o namespace App para a pasta src/:
{
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"require-dev": {
"phpunit/phpunit": "^11.0"
}
}
Execute composer dump-autoload para gerar o autoloading. Agora você pode rodar seu primeiro teste:
vendor/bin/phpunit
2. Escrevendo casos de teste básicos
Vamos criar uma classe Calculadora simples e seus respectivos testes. Primeiro, a classe a ser testada 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.0) {
throw new \InvalidArgumentException('Divisão por zero não é permitida');
}
return $a / $b;
}
}
Agora, o caso de teste em tests/CalculadoraTest.php:
<?php
namespace Tests;
use App\Calculadora;
use PHPUnit\Framework\TestCase;
class CalculadoraTest extends TestCase
{
public function testSomar()
{
$calc = new Calculadora();
$resultado = $calc->somar(2, 3);
$this->assertEquals(5, $resultado);
}
/** @test */
public function soma_com_numeros_negativos()
{
$calc = new Calculadora();
$this->assertEquals(-1, $calc->somar(2, -3));
}
public function testSomarComZero()
{
$calc = new Calculadora();
$this->assertEquals(5, $calc->somar(5, 0));
}
}
Observe que podemos usar o prefixo test ou a anotação @test para identificar métodos de teste. As asserções fundamentais incluem:
assertEquals($expected, $actual)— verifica igualdadeassertTrue($condition)— verifica se é verdadeiroassertFalse($condition)— verifica se é falsoassertNull($variable)— verifica se é nulo
3. Organização e boas práticas com fixtures
Para evitar repetição de código, utilize os métodos setUp() e tearDown():
<?php
namespace Tests;
use App\Calculadora;
use PHPUnit\Framework\TestCase;
class CalculadoraTest extends TestCase
{
private Calculadora $calculadora;
protected function setUp(): void
{
$this->calculadora = new Calculadora();
}
protected function tearDown(): void
{
// Limpeza de recursos, se necessário
unset($this->calculadora);
}
public function testSomar()
{
$this->assertEquals(5, $this->calculadora->somar(2, 3));
}
public function testDividir()
{
$this->assertEquals(2, $this->calculadora->dividir(10, 5));
}
}
Cada teste deve ser independente — a ordem de execução não deve importar. Evite compartilhar estado entre testes.
4. Testando exceções e erros esperados
Para verificar se uma exceção é lançada corretamente, use expectException():
public function testDivisaoPorZeroLancaExcecao()
{
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Divisão por zero não é permitida');
$this->expectExceptionCode(0);
$this->calculadora->dividir(10, 0);
}
Outro exemplo com validação de dados:
public function testEmailInvalidoLancaExcecao()
{
$validador = new Validador();
$this->expectException(\InvalidArgumentException::class);
$this->expectExceptionMessage('Email inválido');
$validador->validarEmail('email-invalido');
}
5. Data providers para testes parametrizados
Data providers eliminam repetição ao testar múltiplos cenários:
<?php
namespace Tests;
use App\Calculadora;
use PHPUnit\Framework\TestCase;
class CalculadoraTest extends TestCase
{
private Calculadora $calculadora;
protected function setUp(): void
{
$this->calculadora = new Calculadora();
}
/** @dataProvider somarDataProvider */
public function testSomarComDataProvider($a, $b, $esperado)
{
$this->assertEquals($esperado, $this->calculadora->somar($a, $b));
}
public static function somarDataProvider(): array
{
return [
'números positivos' => [2, 3, 5],
'números negativos' => [-1, -1, -2],
'com zero' => [5, 0, 5],
'decimais' => [1.5, 2.5, 4.0],
];
}
}
Cada sub-array contém os argumentos para o método de teste. As chaves descritivas ajudam na identificação de falhas.
6. Testando código que depende de recursos externos
Quando uma classe depende de serviços externos (banco de dados, APIs), use stubs e mocks para isolar o teste:
<?php
namespace Tests;
use App\UserService;
use App\UserRepository;
use PHPUnit\Framework\TestCase;
class UserServiceTest extends TestCase
{
public function testBuscarUsuarioPorId()
{
// Cria um stub do repositório
$stub = $this->createStub(UserRepository::class);
// Configura o retorno esperado
$stub->method('findById')
->with(1)
->willReturn(['id' => 1, 'nome' => 'João']);
$service = new UserService($stub);
$usuario = $service->buscar(1);
$this->assertEquals('João', $usuario['nome']);
}
public function testSalvarUsuarioComMock()
{
$mock = $this->createMock(UserRepository::class);
$mock->expects($this->once())
->method('save')
->with($this->callback(function($usuario) {
return $usuario['nome'] === 'Maria';
}));
$service = new UserService($mock);
$service->salvar(['nome' => 'Maria']);
}
}
7. Asserções avançadas e análise de cobertura
PHPUnit oferece asserções especializadas para arrays e objetos:
public function testAssercoesAvancadas()
{
$dados = ['nome' => 'Ana', 'idade' => 30, 'tags' => ['php', 'testes']];
$this->assertCount(3, $dados);
$this->assertContains('php', $dados['tags']);
$this->assertArrayHasKey('nome', $dados);
$this->assertIsArray($dados['tags']);
$usuario = new \stdClass();
$usuario->nome = 'Carlos';
$this->assertInstanceOf(\stdClass::class, $usuario);
$this->assertObjectHasProperty('nome', $usuario);
}
Para gerar relatório de cobertura, execute:
vendor/bin/phpunit --coverage-html coverage
Isso cria uma pasta coverage/ com relatórios detalhados mostrando quais linhas, branches e métodos foram executados durante os testes.
8. Integração contínua e boas práticas finais
Configure a execução automática de testes com GitHub Actions. Crie .github/workflows/phpunit.yml:
name: PHPUnit Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.2'
- name: Install dependencies
run: composer install --prefer-dist
- name: Run tests
run: vendor/bin/phpunit
Personalize a execução com phpunit.xml:
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true"
cacheDirectory=".phpunit.cache">
<testsuites>
<testsuite name="Unit">
<directory>tests</directory>
</testsuite>
</testsuites>
<coverage>
<include>
<directory>src</directory>
</include>
</coverage>
</phpunit>
Checklist de qualidade:
- ✅ Testes rápidos (segundos, não minutos)
- ✅ Isolados (sem dependência entre si)
- ✅ Legíveis (nomes descritivos e data providers)
- ✅ Cobertura significativa (priorize lógica de negócio)
- ✅ Automatizados na CI
Lembre-se: testes não são opcionais — são investimento na qualidade e manutenibilidade do seu código PHP.
Referências
- Documentação oficial do PHPUnit — Guia completo de instalação, configuração e todas as asserções disponíveis
- PHPUnit: Getting Started — Tutorial oficial para iniciantes com exemplos práticos
- Testes com PHPUnit no Laravel — Documentação oficial do Laravel sobre testes, incluindo PHPUnit
- PHP The Right Way: Testing — Seção sobre testes unitários no guia de boas práticas PHP
- PHPUnit: Data Providers — Documentação específica sobre data providers e parametrização de testes
- Mocking com PHPUnit — Guia oficial sobre stubs, mocks e test doubles
- PHPUnit Coverage Analysis — Documentação sobre análise de cobertura de código com exemplos