Guia Completo de PHPUnit na Prática: Testes Unitários em PHP Já leu

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: Crie um arquivo na raiz do seu projeto para configurar o ambiente: Crie a estrutura de pastas: para o código principal e para os testes. Execute para rodar os testes — é isso, você está pronto para começar.

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.

Referências


Artigos relacionados