Testes no Laravel com Pest 5 em 2026: TIA, Mocking e Perguntas de Entrevista

Domine as melhores praticas de testes no Laravel com Pest 5, Test Impact Analysis, Mockery, fakes de facades e testes de arquitetura. Aborda testes unitarios, testes de feature, estrategias de mocking e perguntas comuns de entrevistas para desenvolvedores Laravel.

Representacao visual de testes automatizados no Laravel com Pest PHP cobrindo mocking, testes de arquitetura e mutation testing para preparacao de entrevistas tecnicas

As melhores praticas de testes no Laravel mudaram significativamente com o Pest 5, o motor TIA (Test Impact Analysis) e o PHPUnit 13. O Laravel 13 oferece suporte de primeira classe ao Pest, tornando a experiencia de testes mais expressiva e menos verbosa do que o PHPUnit puro. Este guia aborda os padroes que importam tanto para aplicacoes de producao quanto para entrevistas tecnicas.

Pest 5 e o padrao

O Pest 5 (construido sobre o PHPUnit 13) e o framework de testes padrao do Laravel 13. Ele requer PHP 8.4+ e introduz o motor TIA, verificacao de agentes IA nativa, integracao com PHPStan e sharding balanceado por tempo de execucao.

Configurando o Pest 5 em um Projeto Laravel 13

Cada nova aplicacao Laravel 13 instala o Pest automaticamente. Para projetos existentes, a migracao do Pest 4 leva minutos, ja que a compatibilidade com o PHPUnit 13 e a principal consideracao. A configuracao reside em tests/Pest.php, onde os traits globais e os metodos helpers sao registrados.

tests/Pest.phpphp
use Illuminate\Foundation\Testing\RefreshDatabase;

pest()
    ->extend(Tests\TestCase::class)
    ->use(RefreshDatabase::class)
    ->in('Feature');

Este arquivo unico substitui o antigo trait CreatesApplication e a heranca de classe base. O trait RefreshDatabase envolve cada teste em uma transacao de banco de dados, revertendo as alteracoes automaticamente.

tests/Feature/UserRegistrationTest.phpphp
use App\Models\User;

it('registers a new user with valid data', function () {
    $response = $this->postJson('/api/register', [
        'name' => 'Jane Doe',
        'email' => 'jane@example.com',
        'password' => 'SecurePass123!',
        'password_confirmation' => 'SecurePass123!',
    ]);

    $response->assertStatus(201)
        ->assertJsonStructure(['user' => ['id', 'name', 'email']]);

    expect(User::where('email', 'jane@example.com')->exists())->toBeTrue();
});

A API expect() do Pest se encadeia naturalmente com as assertions do PHPUnit. A chamada assertJsonStructure valida o formato da resposta, enquanto expect()->toBeTrue() confirma o estado do banco de dados. Ambos os estilos coexistem sem conflito.

Test Impact Analysis: A Funcionalidade Principal do Pest 5

O motor TIA e o que torna o Pest 5 transformador para bases de codigo grandes. Na primeira execucao, o Pest registra quais testes tocam quais arquivos. Cada execucao subsequente executa apenas os testes afetados pelas suas alteracoes e reproduz os resultados em cache para todo o resto.

bash
# First run: records baseline (requires PCOV or Xdebug)
php artisan test

# Subsequent runs: only affected tests execute
php artisan test

A suite de testes do Laravel Cloud, contendo mais de 19.000 testes, caiu de aproximadamente tres minutos para cinco segundos com o TIA habilitado. O motor entende mais do que arquivos PHP: ele detecta alteracoes em migrations, templates Blade e componentes JavaScript compartilhados.

O TIA requer um driver de cobertura (PCOV ou Xdebug) para registrar a linha de base. Para pipelines de CI, os mapeamentos em cache persistem entre execucoes, tornando o ganho de desempenho cumulativo.

Testes Unitarios vs Testes de Feature no Laravel

A distincao entre testes unitarios e testes de feature no Laravel determina quais partes do framework sao inicializadas. Os testes unitarios rodam sem o container da aplicacao, tornando-os mais rapidos, mas limitados a logica pura. Os testes de feature inicializam a aplicacao completa, habilitando chamadas HTTP, consultas ao banco de dados e resolucao de servicos.

tests/Unit/PriceCalculatorTest.phpphp
use App\Services\PriceCalculator;

describe('PriceCalculator', function () {
    it('applies a percentage discount correctly', function () {
        $calculator = new PriceCalculator();

        // 20% off a 150.00 base price
        $result = $calculator->applyDiscount(150.00, 20);

        expect($result)->toBe(120.00);
    });

    it('rejects negative discount values', function () {
        $calculator = new PriceCalculator();

        expect(fn () => $calculator->applyDiscount(100.00, -5))
            ->toThrow(InvalidArgumentException::class);
    });
});

Os testes unitarios visam classes isoladas sem dependencias externas. O bloco describe agrupa assertions relacionadas, e o Pest 5 suporta blocos describe aninhados para hierarquias de testes complexas.

Os testes de feature devem representar a maioria de uma suite de testes Laravel. Eles capturam bugs de integracao que os testes unitarios nao detectam, como middleware de rota incorreto, regras de validacao ausentes ou relacionamentos Eloquent quebrados. Uma regra comum: se o codigo toca o banco de dados, a camada HTTP ou uma facade, escreva um teste de feature.

Estrategias de Mocking com Facades e Mockery

O mocking em testes Laravel isola o codigo em teste das dependencias externas. As facades do Laravel fornecem implementacoes fake integradas para filas, eventos, notificacoes, e-mail e storage. O Mockery lida com todo o resto.

tests/Feature/OrderProcessingTest.phpphp
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Queue;
use App\Mail\OrderConfirmation;
use App\Jobs\ProcessPayment;

it('dispatches payment job and sends confirmation email', function () {
    // Fake both Mail and Queue facades
    Mail::fake();
    Queue::fake();

    $user = User::factory()->create();
    $order = Order::factory()->for($user)->create([
        'total' => 99.99,
        'status' => 'pending',
    ]);

    // Act: confirm the order via HTTP
    $this->actingAs($user)
        ->postJson("/api/orders/{$order->id}/confirm")
        ->assertOk();

    // Assert: payment job was dispatched with correct amount
    Queue::assertPushed(ProcessPayment::class, function ($job) use ($order) {
        return $job->order->id === $order->id
            && $job->order->total === 99.99;
    });

    // Assert: confirmation email was sent to the user
    Mail::assertSent(OrderConfirmation::class, function ($mail) use ($user) {
        return $mail->hasTo($user->email);
    });
});

Os fakes de facades interceptam chamadas no nivel do framework, evitando que e-mails reais sejam enviados ou que jobs sejam despachados. As assertions baseadas em closures verificam os dados exatos passados para cada componente.

Mockar nos limites

Evite mockar classes de dominio internas. Mocke nos limites: APIs de terceiros, e-mail, filas, sistemas de arquivos. O excesso de mocking torna os testes frageis e fortemente acoplados aos detalhes de implementacao.

Para dependencias que nao sao facades, injete-as via construtor e use o Mockery:

tests/Feature/PaymentGatewayTest.phpphp
use App\Services\PaymentGateway;
use App\Services\StripeClient;

it('charges the customer through the payment gateway', function () {
    // Create a mock of the Stripe client
    $stripeClient = Mockery::mock(StripeClient::class);
    $stripeClient->shouldReceive('charge')
        ->once()
        ->with('cus_abc123', 5000, 'usd')
        ->andReturn(['status' => 'succeeded', 'id' => 'ch_xyz']);

    // Bind the mock in the container
    $this->app->instance(StripeClient::class, $stripeClient);

    $gateway = app(PaymentGateway::class);
    $result = $gateway->processCharge('cus_abc123', 50.00);

    expect($result['status'])->toBe('succeeded');
});

Vincular o mock com $this->app->instance() substitui a implementacao real pela duracao do teste. O Mockery verifica que charge foi chamado exatamente uma vez com os argumentos esperados.

Testes de Arquitetura com Presets do Pest

O Pest 5 inclui testes de arquitetura que impoe regras estruturais em toda a base de codigo. Esses testes rodam contra a AST, nao em runtime, tornando-os extremamente rapidos.

tests/Architecture/ArchitectureTest.phpphp
arch('controllers do not use Eloquent directly')
    ->expect('App\Http\Controllers')
    ->not->toUse('Illuminate\Database\Eloquent');

arch('services are final classes')
    ->expect('App\Services')
    ->toBeFinal();

arch('no debugging functions in production code')
    ->expect(['dd', 'dump', 'var_dump', 'ray'])
    ->not->toBeUsed();

A primeira regra impede que os controllers consultem o banco de dados diretamente, impondo um padrao de camada de servico. A segunda garante que os servicos nao possam ser estendidos, reduzindo a complexidade de heranca. A terceira detecta instrucoes de debug esquecidas antes que cheguem a producao.

Presets especificos para Laravel tambem estao disponiveis:

tests/Architecture/LaravelPresetTest.phpphp
arch()->preset()->laravel();
arch()->preset()->security();
arch()->preset()->php();

Essas tres linhas impoe dezenas de regras: os models estendem a classe base correta, os controllers nao contem logica de negocios, nao ha funcoes inseguras como eval() ou md5() para hashing, e as convencoes padrao do PHP sao seguidas.

Pronto para mandar bem nas entrevistas de Laravel?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Mutation Testing para Verificar a Qualidade dos Testes

A cobertura de codigo mede quais linhas sao executadas durante os testes. O mutation testing vai alem: ele modifica o codigo fonte e verifica se os testes detectam a alteracao. Se uma mutacao sobrevive, a suite de testes tem uma lacuna.

bash
# Run mutation testing on a specific class
php artisan test --mutate --class=App\\Services\\PriceCalculator

O Pest 5 introduz mutacoes como mudar > para >=, remover instrucoes return e inverter condicoes booleanas. Um score de mutacao abaixo de 80% geralmente indica que os testes verificam saidas sem checar casos limite.

php
// Example: this test has high coverage but low mutation score
it('calculates shipping cost', function () {
    $cost = calculateShipping(weight: 5.0, zone: 'domestic');

    // Only checks that the result is numeric
    expect($cost)->toBeFloat();
});

// Improved: catches mutations by asserting the exact value
it('calculates domestic shipping for 5kg package', function () {
    $cost = calculateShipping(weight: 5.0, zone: 'domestic');

    // Exact assertion catches operator and value mutations
    expect($cost)->toBe(12.50);
});

O primeiro teste passa mesmo se a logica de calculo estiver completamente errada, desde que retorne um float. O segundo teste falha imediatamente quando uma mutacao altera a formula. Assertions especificas produzem scores de mutacao mais altos.

Padroes de Testes de Banco de Dados e Factories

As model factories do Laravel geram dados de teste realistas sem construcao manual de arrays. O Pest 5 combinado com factories produz um setup de dados legivel e sustentavel.

tests/Feature/ArticlePublishingTest.phpphp
use App\Models\Article;
use App\Models\User;

it('publishes a draft article and updates the timestamp', function () {
    $author = User::factory()->create(['role' => 'editor']);

    $article = Article::factory()
        ->for($author, 'author')
        ->draft()
        ->create(['title' => 'Testing Best Practices']);

    $this->actingAs($author)
        ->patchJson("/api/articles/{$article->id}/publish")
        ->assertOk()
        ->assertJsonPath('data.status', 'published');

    $article->refresh();

    expect($article->status)->toBe('published')
        ->and($article->published_at)->not->toBeNull()
        ->and($article->published_at->isToday())->toBeTrue();
});

O estado de factory draft() define valores padrao para artigos nao publicados. As assertions expect() encadeadas com ->and() se leem como linguagem natural e falham com mensagens descritivas.

Testes Paralelos e Sharding

Execute php artisan test --parallel para executar testes em multiplos processos. O Laravel cria automaticamente bancos de dados de teste separados para cada processo, evitando conflitos de dados. O sharding balanceado por tempo do Pest 5 distribui os testes por tempo de execucao real em vez de quantidade, garantindo que cada shard de CI termine ao mesmo tempo.

Perguntas Comuns de Entrevista Tecnica sobre Testes no Laravel

As entrevistas tecnicas para vagas Laravel frequentemente avaliam conhecimentos de testes. Aqui estao as perguntas que aparecem com mais frequencia, junto com o que uma boa resposta deve cobrir.

Qual e a diferenca entre fake(), mock() e spy() em testes Laravel?

O fake() de facade substitui toda a facade por uma implementacao em memoria (Mail::fake, Queue::fake). mock() via Mockery define expectativas antes da execucao e falha se nao forem atendidas. spy() registra interacoes e permite assertions apos a execucao, sem definir expectativas previas. A escolha depende de se o teste precisa verificar comportamento (mock), registrar interacoes (spy) ou prevenir efeitos colaterais (fake).

Como o RefreshDatabase difere do DatabaseTransactions?

O RefreshDatabase executa as migrations uma vez e envolve cada teste em uma transacao. O DatabaseTransactions assume que o banco de dados ja tem o schema correto e apenas envolve os testes em transacoes. O RefreshDatabase e mais seguro para pipelines de CI onde o banco de dados pode nao existir. O DatabaseTransactions e mais rapido quando o schema esta garantido como atualizado.

Quando os testes de feature devem ser preferidos em relacao aos testes unitarios?

Os testes de feature devem cobrir qualquer codigo que interaja com a camada HTTP, banco de dados ou servicos do Laravel. Os testes unitarios sao reservados para funcoes puras e value objects sem dependencias do framework. Em uma aplicacao Laravel tipica, os testes de feature superam os testes unitarios em uma proporcao de aproximadamente 3:1 ou mais. Isso reflete a realidade de que a maioria do codigo Laravel esta inerentemente integrada com o framework.

Como o Test Impact Analysis melhora o desempenho do CI?

O TIA registra quais testes tocam quais arquivos fonte durante a primeira execucao. Nas execucoes seguintes, o Pest executa apenas os testes afetados por arquivos alterados e reproduz resultados em cache para testes inalterados. Suites grandes que levavam minutos podem ser concluidas em segundos. O tradeoff: o TIA requer um driver de cobertura (PCOV ou Xdebug) para construir o mapa de dependencias, e a primeira execucao leva mais tempo do que uma execucao normal.

Como o mutation testing melhora a qualidade dos testes alem da cobertura de codigo?

A cobertura de codigo mede os caminhos de execucao. Um teste pode alcancar 100% de cobertura chamando cada metodo sem afirmar nada significativo. O mutation testing modifica o codigo fonte (alterando operadores, removendo returns, invertendo booleanos) e verifica que pelo menos um teste falhe. Mutacoes que sobrevivem revelam assertions muito amplas ou ausentes. Executar php artisan test --mutate produz uma porcentagem de score de mutacao junto com a cobertura padrao.

Para mais perguntas de entrevista Laravel, o banco de questoes do SharpSkill cobre autenticacao, padroes do service container, relacionamentos Eloquent e arquitetura de filas.

Testando Respostas HTTP e Assertions JSON

Os helpers de teste HTTP do Laravel verificam codigos de status, headers, estruturas JSON e destinos de redirecionamento. Combinados com o Pest, eles formam testes de integracao concisos.

tests/Feature/ApiAuthenticationTest.phpphp
use App\Models\User;

describe('API Authentication', function () {
    it('rejects unauthenticated requests with 401', function () {
        $this->getJson('/api/profile')
            ->assertUnauthorized();
    });

    it('returns the authenticated user profile', function () {
        $user = User::factory()->create([
            'name' => 'John Doe',
            'email' => 'john@example.com',
        ]);

        $this->actingAs($user)
            ->getJson('/api/profile')
            ->assertOk()
            ->assertJson([
                'data' => [
                    'name' => 'John Doe',
                    'email' => 'john@example.com',
                ],
            ]);
    });

    it('validates required fields on registration', function () {
        $this->postJson('/api/register', [])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['name', 'email', 'password']);
    });
});

O bloco describe agrupa testes relacionados a autenticacao. Cada nome de teste descreve o comportamento esperado, nao a implementacao. O metodo assertJsonValidationErrors verifica que campos especificos tem mensagens de erro de validacao.

Fontes

Pontos Essenciais sobre Testes no Laravel com Pest 5

  • O Pest 5 com Laravel 13 elimina o boilerplate atraves de uma API de configuracao fluida, cadeias expect() e descoberta automatica de testes
  • O motor TIA re-executa apenas testes afetados, reduzindo a execucao de grandes suites de minutos para segundos sem sacrificar a precisao da cobertura
  • Os testes de feature devem formar o nucleo de uma suite de testes Laravel, com testes unitarios reservados para logica de negocios isolada
  • Os fakes de facades (Mail::fake(), Queue::fake()) lidam com o mocking no nivel do framework, enquanto o Mockery lida com dependencias de terceiros injetadas via container
  • Os testes de arquitetura impoe regras estruturais (sem Eloquent em controllers, sem funcoes de debug) sem custo de runtime
  • O mutation testing com --mutate detecta assertions fracas que a cobertura de codigo sozinha nao detecta
  • Os estados de factory e as assertions encadeadas expect()->and() mantem o setup de dados de teste legivel e as assertions especificas
  • Para a preparacao para entrevistas Laravel, entender mock/fake/spy, o comportamento do RefreshDatabase, TIA e mutation testing demonstra maturidade em testes alem da simples cobertura

Comece a praticar!

Teste seus conhecimentos com nossos simuladores de entrevista e testes tecnicos.

Desafio do dia

Você saberia encontrar o bug em Laravel?

Um trecho real, um bug escondido, uma tentativa por dia. Sem conta para testar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador da SharpSkill

Desenvolvedor fullstack há mais de 10 anos. Dirige a SharpSkill e responde por tudo o que é publicado aqui.

Atualizado em 2 de setembro de 2026

Tags

#laravel
#testing
#pest
#php
#interview

Compartilhar

Artigos relacionados