<h2>O que é PHPUnit e Por Que Usar</h2>
<p>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.</p>
<p>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.</p>
<h2>Instalação e Configuração Inicial</h2>
<p>A forma mais prática de instalar PHPUnit é via Composer. Execute:</p>
<pre><code class="language-bash">composer require --dev phpunit/phpunit ^10</code></pre>
<p>Crie um arquivo <code>phpunit.xml</code> na raiz do seu projeto para configurar o ambiente:</p>
<pre><code class="language-xml"><?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></code></pre>
<p>Crie a estrutura de pastas: <code>src/</code> para o código principal e <code>tests/Unit/</code> para os testes. Execute <code>vendor/bin/phpunit</code> para rodar os testes — é isso, você está pronto para começar.</p>
<h2>Escrevendo Seu Primeiro Teste</h2>
<h3>Exemplo Prático: Testando uma Calculadora</h3>
<p>Criaremos uma classe <code>Calculadora</code> simples e seus testes. Primeiro, a classe em <code>src/Calculadora.php</code>:</p>
<pre><code class="language-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;
}
}</code></pre>
<p>Agora o teste em <code>tests/Unit/CalculadoraTest.php</code>:</p>
<pre><code class="language-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);
}
}</code></pre>
<p>Execute <code>vendor/bin/phpunit tests/Unit/CalculadoraTest.php</code>. Todos os testes devem passar. Observe a estrutura: cada método <code>test_*</code> é um teste isolado. O método <code>setUp()</code> é executado antes de cada teste, permitindo reutilizar o objeto <code>$this->calc</code>.</p>
<h3>Assertions Comuns</h3>
<p>As asserções são o coração do teste — elas verificam se o resultado está correto:</p>
<pre><code class="language-php">$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</code></pre>
<h2>Testes Avançados: Mocks e Stubs</h2>
<h3>Isolando Dependências com Mocks</h3>
<p>Código real frequentemente depende de outros objetos. Para testar isoladamente, usamos mocks — objetos falsos que simulam comportamento. Imagine uma classe <code>UsuarioService</code> que chama um banco de dados:</p>
<pre><code class="language-php"><?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']);
}
}</code></pre>
<p>Teste com mock em <code>tests/Unit/UsuarioServiceTest.php</code>:</p>
<pre><code class="language-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);
}
}</code></pre>
<p>O mock simula o repositório sem acessar o banco de dados real. <code>expects($this->once())</code> valida que o método foi chamado exatamente uma vez. <code>willReturn()</code> define o retorno simulado. Isso garante que seu teste é rápido, isolado e confiável.</p>
<h2>Boas Práticas e Padrões</h2>
<h3>O Padrão AAA</h3>
<p>Todo bom teste segue o padrão <strong>Arrange-Act-Assert</strong> (Preparar-Agir-Verificar):</p>
<pre><code class="language-php">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);
}</code></pre>
<h3>Nomes Descritivos</h3>
<p>Evite nomes genéricos como <code>test_ok()</code>. Use <code>test_calcula_juros_corretamente()</code> — o nome documenta o comportamento esperado.</p>
<blockquote><p><strong>Dica profissional:</strong> 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.</p></blockquote>
<h3>Cobertura de Testes</h3>
<p>Verifique quantas linhas do seu código estão sendo testadas:</p>
<pre><code class="language-bash">vendor/bin/phpunit --coverage-html coverage/</code></pre>
<p>Abra <code>coverage/index.html</code> no navegador. Almeje 80-90% de cobertura — 100% é impraticável e nem sempre necessário.</p>
<h2>Conclusão</h2>
<p>PHPUnit transforma desenvolvimento PHP de um processo arriscado em uma prática profissional confiável. Comece com testes simples usando <code>assertEquals()</code> e <code>assertTrue()</code>, evoluindo para mocks quando suas dependências crescerem. Lembre-se: testes devem ser rápidos, isolados e escritos <strong>antes</strong> ou <strong>junto com</strong> 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.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://phpunit.de/documentation.html" target="_blank" rel="noopener noreferrer">Documentação Oficial PHPUnit</a></li>
<li><a href="https://www.php.net/manual/pt_BR/" target="_blank" rel="noopener noreferrer">PHP: Testes Unitários com PHPUnit</a></li>
<li><a href="https://www.oreilly.com/library/view/test-driven-development/0201616416/" target="_blank" rel="noopener noreferrer">Test Driven Development: By Example - Kent Beck</a></li>
<li><a href="https://www.oreilly.com/library/view/working-effectively-with/0131177052/" target="_blank" rel="noopener noreferrer">Working Effectively with Legacy Code - Michael Feathers</a></li>
<li><a href="https://www.php-fig.org/psr/psr-12/" target="_blank" rel="noopener noreferrer">PSR-12: Extended Coding Style Guide</a></li>
</ul>