<h2>PHPUnit: Fundamentos e Configuração</h2>
<p>PHPUnit é o framework de testes mais consolidado no ecossistema PHP. Ele oferece uma estrutura robusta para escrever testes unitários, de integração e funcionais. Em Laravel, PHPUnit vem pré-configurado no arquivo <code>phpunit.xml</code>, permitindo que você comece a escrever testes imediatamente. A configuração padrão já cobre a maioria dos cenários, incluindo ambiente de teste separado e carregamento automático de fixtures.</p>
<p>Para começar, crie um teste simples na pasta <code>tests/Unit</code>. Todo teste em PHPUnit herda de <code>TestCase</code> e cada método de teste deve começar com <code>test</code>. Aqui está um exemplo prático:</p>
<pre><code class="language-php"><?php
namespace Tests\Unit;
use PHPUnit\Framework\TestCase;
class CalculadoraTest extends TestCase
{
public function testSomaDoisNumeros()
{
$resultado = 2 + 2;
$this->assertEquals(4, $resultado);
}
public function testDivisaoPorZeroLancaExcecao()
{
$this->expectException(\DivisionByZeroError::class);
$resultado = 1 / 0;
}
}</code></pre>
<p>Execute com <code>php artisan test</code> ou <code>vendor/bin/phpunit</code>. O PHPUnit oferece várias asserções como <code>assertEquals()</code>, <code>assertTrue()</code>, <code>assertStringContainsString()</code> e muitas outras para validar comportamentos esperados.</p>
<h2>Testando Controllers e Models com PHPUnit</h2>
<p>Em testes de feature no Laravel, você precisará testar controllers, models e a integração entre eles. A classe <code>Tests\TestCase</code> fornece helpers como <code>$this->get()</code>, <code>$this->post()</code> e <code>$this->json()</code> para fazer requisições HTTP simuladas. Aqui está um exemplo realista:</p>
<pre><code class="language-php"><?php
namespace Tests\Feature;
use App\Models\User;
use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
class UserControllerTest extends TestCase
{
use RefreshDatabase;
public function testListaUsuarios()
{
User::factory()->count(3)->create();
$response = $this->get('/api/users');
$response->assertStatus(200);
$response->assertJsonCount(3);
}
public function testCriaUsuarioComDadosValidos()
{
$dados = [
'name' => 'João Silva',
'email' => 'joao@example.com',
'password' => 'senha123456',
];
$response = $this->post('/api/users', $dados);
$response->assertStatus(201);
$this->assertDatabaseHas('users', ['email' => 'joao@example.com']);
}
public function testRejeitaDadosInvalidos()
{
$response = $this->post('/api/users', ['name' => 'João']);
$response->assertStatus(422);
$response->assertJsonValidationErrors(['email', 'password']);
}
}</code></pre>
<p>Utilize <code>RefreshDatabase</code> para resetar o banco em cada teste. Use factories para gerar dados de teste consistentes. As asserções <code>assertStatus()</code>, <code>assertJson()</code> e <code>assertDatabaseHas()</code> verificam respostas e estado do banco simultaneamente.</p>
<h2>Pest: Sintaxe Moderna e Expressiva</h2>
<p>Pest é um framework de testes moderno construído sobre PHPUnit que oferece uma sintaxe mais limpa e intuitiva. Apesar de ser mais recente, é totalmente compatível com PHPUnit e oferece experiência superior em desenvolvimento. A instalação é simples: <code>composer require pestphp/pest --dev</code>.</p>
<p>A principal diferença está na sintaxe. Em vez de classes e métodos, Pest usa funções globais <code>test()</code> e <code>it()</code>. Isso torna os testes mais legíveis e próximos da linguagem natural:</p>
<pre><code class="language-php"><?php
use App\Models\Post;
use Tests\TestCase;
uses(TestCase::class)->in('Feature');
test('usuário autenticado pode criar post', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)
->post('/api/posts', [
'title' => 'Meu Post',
'content' => 'Conteúdo do post',
]);
$response->assertStatus(201);
expect(Post::count())->toBe(1);
});
it('rejeita posts com título vazio', function () {
$user = User::factory()->create();
$response = $this->actingAs($user)
->post('/api/posts', ['content' => 'Conteúdo']);
$response->assertStatus(422);
});</code></pre>
<p>Pest também introduz expectativas fluentes com <code>expect()</code>, que são mais expressivas que asserções tradicionais. Você pode encadear múltiplas verificações: <code>expect($resultado)->toBeInt()->toBeGreaterThan(0)</code>.</p>
<h3>Fixtures e Setup com Pest</h3>
<p>Pest permite definir setup compartilhado de forma elegante:</p>
<pre><code class="language-php"><?php
beforeEach(function () {
$this->user = User::factory()->create();
$this->admin = User::factory()->admin()->create();
});
test('admin pode deletar qualquer post', function () {
$post = Post::factory()->create();
$response = $this->actingAs($this->admin)
->delete("/api/posts/{$post->id}");
$response->assertStatus(204);
expect(Post::count())->toBe(0);
});</code></pre>
<p>Esse approach com <code>beforeEach()</code> é mais intuitivo que <code>setUp()</code> no PHPUnit tradicional. Pest também suporta <code>afterEach()</code>, <code>beforeAll()</code> e <code>afterAll()</code> para controle fino do ciclo de vida dos testes.</p>
<h2>Melhores Práticas e Diferenças</h2>
<p>A escolha entre PHPUnit e Pest não é binária. PHPUnit permanece o padrão da indústria, com documentação extensa e ampla integração em IDEs. Pest é ideal para novos projetos ou times que valorizam legibilidade extrema. Ambas coexistem bem no mesmo projeto.</p>
<p>Independente da escolha, siga estas práticas: 1) Teste comportamento, não implementação — focalize no "o que" não no "como"; 2) Use factories e seeders para dados consistentes; 3) Mantenha testes independentes, sem compartilhamento de estado; 4) Nomeie testes descritivamente; 5) Aim para 80%+ de cobertura em código crítico, não em tudo.</p>
<pre><code class="language-php">// PHPUnit
$this->assertEquals($esperado, $resultado);
// vs Pest
expect($resultado)->toBe($esperado);</code></pre>
<p>Ambas as abordagens são válidas. A escolha depende da preferência da equipe e do contexto do projeto. Em projetos legados, PHPUnit faz mais sentido. Em startups ágeis, Pest acelera a entrega.</p>
<h2>Conclusão</h2>
<p>PHPUnit e Pest são ambos ferramentas poderosas para garantir qualidade no Laravel. PHPUnit oferece solidez e compatibilidade, ideal para projetos estabelecidos. Pest traz modernidade e sintaxe intuitiva, perfeito para times que priorizam legibilidade. O fundamental é <strong>escrever testes</strong>, não qual framework escolher. Comece pequeno, teste comportamentos críticos, e expanda gradualmente sua cobertura de testes conforme o projeto cresce.</p>
<h2>Referências</h2>
<ul>
<li><a href="https://laravel.com/docs/testing" target="_blank" rel="noopener noreferrer">Laravel Testing Documentation</a></li>
<li><a href="https://phpunit.de/documentation.html" target="_blank" rel="noopener noreferrer">PHPUnit Official Manual</a></li>
<li><a href="https://pestphp.com" target="_blank" rel="noopener noreferrer">Pest PHP - Modern Testing Framework</a></li>
<li><a href="https://github.com/alexeymezenin/laravel-best-practices#testing" target="_blank" rel="noopener noreferrer">Laravel Best Practices - Testing</a></li>
<li><a href="https://laracasts.com/series/test-driven-laravel" target="_blank" rel="noopener noreferrer">Test Driven Development in Laravel</a></li>
</ul>