Laravel Testes com PHPUnit e Pest: Do Básico ao Avançado Já leu

PHPUnit: Fundamentos e Configuração 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 , 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. Para começar, crie um teste simples na pasta . Todo teste em PHPUnit herda de e cada método de teste deve começar com . Aqui está um exemplo prático: Execute com ou . O PHPUnit oferece várias asserções como , , e muitas outras para validar comportamentos esperados. Testando Controllers e Models com PHPUnit Em testes de feature no Laravel, você precisará testar controllers, models e a integração entre eles. A classe fornece helpers como , e para fazer requisições HTTP simuladas. Aqui está um exemplo realista: Utilize para resetar o banco em cada teste. Use factories

PHPUnit: Fundamentos e Configuração

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 phpunit.xml, 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.

Para começar, crie um teste simples na pasta tests/Unit. Todo teste em PHPUnit herda de TestCase e cada método de teste deve começar com test. Aqui está um exemplo prático:

<?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;
    }
}

Execute com php artisan test ou vendor/bin/phpunit. O PHPUnit oferece várias asserções como assertEquals(), assertTrue(), assertStringContainsString() e muitas outras para validar comportamentos esperados.

Testando Controllers e Models com PHPUnit

Em testes de feature no Laravel, você precisará testar controllers, models e a integração entre eles. A classe Tests\TestCase fornece helpers como $this->get(), $this->post() e $this->json() para fazer requisições HTTP simuladas. Aqui está um exemplo realista:

<?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']);
    }
}

Utilize RefreshDatabase para resetar o banco em cada teste. Use factories para gerar dados de teste consistentes. As asserções assertStatus(), assertJson() e assertDatabaseHas() verificam respostas e estado do banco simultaneamente.

Pest: Sintaxe Moderna e Expressiva

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: composer require pestphp/pest --dev.

A principal diferença está na sintaxe. Em vez de classes e métodos, Pest usa funções globais test() e it(). Isso torna os testes mais legíveis e próximos da linguagem natural:

<?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);
});

Pest também introduz expectativas fluentes com expect(), que são mais expressivas que asserções tradicionais. Você pode encadear múltiplas verificações: expect($resultado)->toBeInt()->toBeGreaterThan(0).

Fixtures e Setup com Pest

Pest permite definir setup compartilhado de forma elegante:

<?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);
});

Esse approach com beforeEach() é mais intuitivo que setUp() no PHPUnit tradicional. Pest também suporta afterEach(), beforeAll() e afterAll() para controle fino do ciclo de vida dos testes.

Melhores Práticas e Diferenças

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.

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.

// PHPUnit
$this->assertEquals($esperado, $resultado);
// vs Pest
expect($resultado)->toBe($esperado);

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.

Conclusão

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 é escrever testes, não qual framework escolher. Comece pequeno, teste comportamentos críticos, e expanda gradualmente sua cobertura de testes conforme o projeto cresce.

Referências


Artigos relacionados