Introdução: Por que os testes são necessários em PHP?
\n\nDesenvolver em PHP sem testes é como construir uma casa sem alicerce. Mais cedo ou mais tarde, a aplicação cairá e os usuários verão a "tela branca da morte". Testar o código não é um luxo, mas uma necessidade para qualquer projeto sério. Isso permite:
\n- \n
- Detectar erros em estágios iniciais — antes que o código vá para produção. \n
- Simplificar a refatoração — você pode alterar a arquitetura com segurança, sabendo que os testes detectarão regressões. \n
- Criar código autodocumentado — os testes servem como documentação viva de como o sistema deve funcionar. \n
- Aumentar a confiança da equipe — cada novo commit passa por verificação automática. \n
Em PHP, existem três níveis principais de teste: Unit (unitário), Integration (integração) e E2E (ponta a ponta). Neste artigo, abordaremos cada um deles com exemplos práticos, desde simples asserts até testes completos de navegador.
\n\n1. Testes Unitários: testando os "blocos" da aplicação
\n\nO que é um teste unitário?
\nUm teste unitário verifica o funcionamento de um pequeno bloco de código — geralmente um método ou função — em completo isolamento de dependências externas (banco de dados, API, sistema de arquivos).
\n\nFerramentas para PHP
\n- \n
- PHPUnit — o padrão de facto. Usado em Laravel, Symfony e muitos CMS. \n
- Pest — uma alternativa moderna com sintaxe mais concisa, construída sobre o PHPUnit. \n
Exemplo: testando uma calculadora de descontos
\nSuponha que temos uma classe simples DiscountCalculator:
<?php\n\nclass DiscountCalculator\n{\n public function calculate(float $price, float $percent): float\n {\n if ($percent < 0 || $percent > 100) {\n throw new \\InvalidArgumentException('A porcentagem deve estar entre 0 e 100');\n }\n return $price - ($price * $percent / 100);\n }\n}\n\n\nAgora, vamos escrever um teste unitário usando PHPUnit:
\n\n<?php\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass DiscountCalculatorTest extends TestCase\n{\n private DiscountCalculator $calculator;\n\n protected function setUp(): void\n {\n $this->calculator = new DiscountCalculator();\n }\n\n public function testCalculateWithZeroPercent(): void\n {\n $result = $this->calculator->calculate(100, 0);\n $this->assertEquals(100, $result);\n }\n\n public function testCalculateWithFiftyPercent(): void\n {\n $result = $this->calculator->calculate(200, 50);\n $this->assertEquals(100, $result);\n }\n\n public function testCalculateThrowsExceptionOnInvalidPercent(): void\n {\n $this->expectException(\\InvalidArgumentException::class);\n $this->calculator->calculate(100, 150);\n }\n}\n\n\nPontos-chave:
\n- \n
- Cada método testa apenas um cenário. \n
- Não acessamos banco de dados ou serviços externos. \n
- Usamos
setUp()para inicializar objetos comuns. \n
Melhores práticas de testes unitários
\n- \n
- Siga o princípio FIRST: Fast, Isolated, Repeatable, Self-validating, Timely. \n
- Nomeie os testes como "test_ação_resultadoEsperado". \n
- Evite lógica nos testes — nada de if/switch/loops. \n
2. Testes de Integração: verificando a interação entre componentes
\n\nQual a diferença do Unit?
\nOs testes de integração verificam como diferentes partes do sistema funcionam juntas. Por exemplo, como seu serviço interage com um banco de dados real, cache ou API externa.
\n\nExemplo: testando um repositório com banco de dados (Laravel + SQLite)
\nSuponha que temos um <