Testes no Laravel em 2026: Pest, Mocking e Perguntas de Entrevistas Tecnicas

Artigo completo sobre testes no Laravel com Pest PHP em 2026. Aborda testes unitarios, mocking com facades, testes de arquitetura, mutation testing e as perguntas mais cobradas em entrevistas tecnicas para desenvolvedores PHP.

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

Processos seletivos para desenvolvedores Laravel em 2026 elevaram drasticamente a exigencia tecnica em torno de testes automatizados. Empresas brasileiras e internacionais nao contratam mais quem nao sabe escrever testes com Pest, configurar mocks de facades ou explicar a diferenca entre teste unitario e teste de feature. O ecossistema Laravel amadureceu ao ponto de oferecer ferramentas nativas para cada camada de validacao, e o mercado espera que profissionais de nivel pleno e senior dominem todas elas. Este artigo reune os conceitos, padroes e exemplos de codigo que aparecem com maior frequencia nas avaliacoes tecnicas, organizados de forma progressiva para quem quer se preparar de verdade.

Por que Pest se tornou o padrao de testes no Laravel?

O Pest PHP foi adotado como framework de testes padrao do ecossistema Laravel a partir da versao 11. A sintaxe expressiva com closures, a eliminacao de classes boilerplate e recursos exclusivos como testes de arquitetura e mutation testing fizeram dele a escolha natural da comunidade. Em entrevistas tecnicas para vagas Laravel em 2026, o conhecimento de Pest deixou de ser diferencial e se tornou requisito. Recrutadores esperam fluencia pratica, nao apenas familiaridade teorica.

Configuracao do Pest: O Ponto de Partida

Antes de escrever qualquer teste, a configuracao do Pest precisa estar ajustada corretamente. O arquivo tests/Pest.php funciona como ponto central de setup, onde se define a classe base dos testes e os traits que serao aplicados automaticamente a cada arquivo de teste dentro de um diretorio especifico.

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

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

O metodo extend() vincula todos os testes a classe TestCase do Laravel, garantindo acesso ao container de servicos, ao client HTTP de testes e a toda infraestrutura do framework. O trait RefreshDatabase garante que cada teste rode com um banco de dados limpo, executando migrations e revertendo transacoes automaticamente. A chamada in('Feature') restringe essa configuracao ao diretorio de testes de feature, deixando os testes unitarios livres de dependencias do framework.

Essa separacao e um ponto que entrevistadores exploram com frequencia. Testes unitarios devem ser rapidos e isolados, sem depender do banco de dados ou do container. Testes de feature, por outro lado, exercitam o fluxo completo da aplicacao e precisam de toda a infraestrutura do Laravel disponivel.

Testes de Feature: Validando Fluxos HTTP Completos

O teste de feature e a categoria mais cobrada em entrevistas Laravel. Diferente do teste unitario puro, que valida logica de negocio isolada, o teste de feature dispara requisicoes HTTP reais contra a aplicacao, verificando status codes, estrutura de respostas JSON e efeitos colaterais no banco de dados.

O cenario classico de registro de usuario ilustra bem o padrao esperado por recrutadores. O teste envia dados via POST, verifica a resposta HTTP e confirma que o usuario foi persistido no banco.

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

O detalhe que separa candidatos preparados de candidatos medianos esta na combinacao de assercoes HTTP com expectativas do Pest. O assertStatus(201) valida o contrato da API, o assertJsonStructure confirma que a resposta segue o formato esperado, e o expect()->toBeTrue() verifica o efeito colateral no banco de dados. Esse padrao triplo demonstra rigor ao entrevistador.

Outro aspecto frequentemente explorado e o uso de postJson em vez de post. O metodo postJson configura automaticamente os headers Content-Type e Accept como application/json, simulando o comportamento real de um cliente frontend ou mobile. Candidatos que usam post sem configurar headers revelam falta de experiencia com APIs REST.

Testes Unitarios com Pest: Logica de Negocio Isolada

Enquanto testes de feature exercitam o fluxo HTTP completo, testes unitarios focam exclusivamente na logica de negocio sem dependencias externas. No Laravel, isso significa testar services, value objects, calculadoras e qualquer classe que nao dependa diretamente do framework.

O bloco describe do Pest permite agrupar testes relacionados de forma semantica, criando uma estrutura legivel que funciona como documentacao viva do comportamento esperado.

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

A assercao toBe(120.00) verifica o valor exato do resultado, garantindo que a formula de calculo esta correta. O teste de excecao com toThrow valida que a classe protege seus invariantes, rejeitando entradas invalidas com excecoes tipadas. Esse tipo de validacao defensiva e algo que recrutadores consideram sinal de maturidade tecnica.

Um erro comum entre candidatos menos experientes e instanciar o service com dependencias reais dentro de testes unitarios. Se o PriceCalculator dependesse de um repositorio ou de uma API externa, o teste deveria usar mocks. A regra e simples: se a dependencia faz I/O (banco, rede, filesystem), ela deve ser mockada. Se e pura computacao, pode ser usada diretamente.

Mocking de Facades e Servicos: Isolando Efeitos Colaterais

O sistema de facades do Laravel oferece uma interface elegante para mocking que nao exige configuracao manual do container. Os metodos fake() das facades Mail, Queue, Notification, Event e Storage substituem a implementacao real por um double que registra todas as chamadas recebidas sem executar nenhum efeito colateral.

O cenario de processamento de pedidos a seguir demonstra como verificar que um job foi despachado e que um email foi enviado, sem processar pagamentos reais nem disparar emails de verdade.

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

A sequencia Arrange-Act-Assert fica explicitamente clara neste teste. Na fase de arranjo, facades sao substituidas por fakes e os modelos sao criados via factories. Na acao, uma requisicao HTTP confirma o pedido. Nas assercoes, Queue::assertPushed e Mail::assertSent verificam que os efeitos colaterais ocorreram com os parametros corretos.

Em entrevistas, recrutadores costumam perguntar por que se usa Mail::fake() no inicio do teste em vez de no setUp. A resposta e que o fake deve ser ativado apenas nos testes que precisam dele. Ativar globalmente pode mascarar bugs em testes que dependem de emails realmente sendo enfileirados. Essa nuance demonstra experiencia pratica com suites de teste de larga escala.

Mocking com Mockery: Substituindo Dependencias do Container

Quando a dependencia nao e uma facade do Laravel, o mocking precisa ser feito manualmente com Mockery e o service container. Esse padrao e essencial para testar services que dependem de clientes HTTP, gateways de pagamento ou qualquer integracao externa.

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

O fluxo segue tres etapas criticas. Primeiro, o mock do StripeClient e criado com Mockery, definindo que o metodo charge deve ser chamado exatamente uma vez com parametros especificos. Segundo, o mock e registrado no container do Laravel via $this->app->instance(), substituindo a implementacao real. Terceiro, o PaymentGateway e resolvido pelo container, recebendo automaticamente o mock injetado.

A multiplicacao do valor por 100 (de 50.00 para 5000) e um detalhe que entrevistadores adoram explorar. APIs de pagamento como Stripe trabalham com valores em centavos para evitar problemas de arredondamento com ponto flutuante. Candidatos que percebem essa conversao sem que ela seja apontada demonstram familiaridade com integracoes reais de pagamento.

A verificacao ->once() e tao importante quanto a assercao do resultado. Ela garante que o service nao esta fazendo chamadas duplicadas ao gateway de pagamento, algo que poderia causar cobranccas indevidas em producao.

Pronto para mandar bem nas entrevistas de Laravel?

Pratique com nossos simuladores interativos, flashcards e testes tecnicos.

Testes de Arquitetura: Protegendo as Regras do Projeto

Os testes de arquitetura sao um recurso exclusivo do Pest que nao existe em nenhum outro framework de testes PHP. Eles validam a estrutura do codigo em vez do comportamento, garantindo que convencoes arquiteturais sejam respeitadas automaticamente.

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();

O primeiro teste impede que controllers acessem o Eloquent diretamente, forcando o uso de services ou repositories como camada intermediaria. Essa regra elimina o acoplamento direto entre a camada HTTP e o banco de dados, facilitando testes e refatoracoes futuras.

O segundo teste garante que todas as classes dentro de App\Services sao declaradas como final. Classes finais nao podem ser estendidas por heranca, incentivando composicao em vez de hierarquias complexas. Esse padrao alinha o codigo com principios SOLID e demonstra maturidade arquitetural.

O terceiro teste varre todo o codebase em busca de funcoes de depuracao que jamais deveriam chegar a producao. Um dd() esquecido pode derrubar um endpoint inteiro, e funcoes como var_dump indicam praticas de debug improvisadas que comprometem a qualidade do projeto.

O Laravel e o Pest tambem disponibilizam presets prontos que cobrem dezenas de regras arquiteturais com uma unica linha de codigo.

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

O preset laravel() verifica convencoes especificas do framework, como a correta utilizacao de controllers, models e middlewares. O preset security() detecta vulnerabilidades comuns, como uso de funcoes inseguras (eval, exec, shell_exec). O preset php() valida boas praticas gerais da linguagem. Combinados, esses tres presets formam uma rede de protecao que impede regressoes arquiteturais silenciosas.

Em entrevistas, demonstrar conhecimento sobre testes de arquitetura e um diferencial significativo. A maioria dos candidatos conhece apenas testes unitarios e de feature. Quem menciona e explica arch tests mostra visao sistemica sobre qualidade de software.

Mutation Testing: Medindo a Qualidade Real dos Testes

Cobertura de codigo e uma metrica enganosa quando usada isoladamente. Um teste que verifica apenas que um metodo retorna um float tem alta cobertura de linhas, mas nao detecta bugs reais. O mutation testing resolve esse problema introduzindo alteracoes deliberadas no codigo fonte (mutacoes) e verificando se os testes falham. Se um teste continua passando apos uma mutacao, significa que ele nao esta testando o comportamento real da aplicacao.

O Pest oferece mutation testing integrado, sem necessidade de ferramentas externas.

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

O resultado mostra quais mutacoes foram detectadas (killed) e quais sobreviveram (survived). Mutacoes sobreviventes indicam testes fracos que precisam de assercoes mais especificas.

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 verifica apenas o tipo do retorno. Se o mutador trocar um operador de multiplicacao por divisao dentro de calculateShipping, o resultado continuara sendo float e o teste passara. O segundo teste verifica o valor exato, detectando qualquer alteracao na logica de calculo.

Recrutadores que perguntam sobre mutation testing estao avaliando se o candidato entende a diferenca entre cobertura quantitativa e qualitativa. Um mutation score de 90% ou mais indica que a suite de testes realmente protege o comportamento da aplicacao, nao apenas suas linhas de codigo.

Cenarios Classicos de Entrevista: Testes Completos

Alem dos conceitos isolados, entrevistas tecnicas para vagas Laravel frequentemente apresentam cenarios completos que o candidato deve implementar ou analisar. Os dois exemplos a seguir cobrem padroes que aparecem com alta frequencia.

O primeiro cenario envolve a publicacao de um artigo, combinando autenticacao, mutacao de estado e verificacao de timestamps.

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 uso de ->for($author, 'author') vincula o artigo ao usuario via relacionamento Eloquent, e ->draft() aplica um state da factory que configura o artigo como rascunho. O assertJsonPath verifica um campo especifico na resposta JSON sem precisar validar toda a estrutura. Apos a requisicao, $article->refresh() recarrega o modelo do banco para confirmar que as alteracoes foram persistidas corretamente.

A cadeia de expectativas com ->and() do Pest e uma sintaxe elegante que permite multiplas assercoes sobre o mesmo subject sem repetir expect(). Isso torna o teste mais legivel e demonstra dominio da API do framework.

O segundo cenario cobre autenticacao de API, um tema universal em qualquer entrevista backend.

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 tres cenarios complementares: rejeicao de requisicao nao autenticada, retorno de perfil para usuario autenticado e validacao de campos obrigatorios no registro. Essa cobertura tripla (caminho negativo, caminho positivo e validacao) demonstra ao entrevistador que o candidato pensa em todos os cenarios relevantes, nao apenas no happy path.

O metodo assertUnauthorized() e um alias para assertStatus(401), mas comunica a intencao do teste de forma mais clara. Da mesma forma, assertUnprocessable() corresponde ao status 422 e assertJsonValidationErrors verifica que os erros de validacao seguem o formato padrao do Laravel.

O que os Recrutadores Realmente Perguntam

Qual a diferenca entre RefreshDatabase e DatabaseTransactions?

O RefreshDatabase executa todas as migrations antes da suite de testes e usa transacoes para isolar cada teste individual. O DatabaseTransactions assume que o banco ja esta migrado e apenas envolve cada teste em uma transacao que e revertida ao final. Em pipelines de CI que partem de um banco limpo, RefreshDatabase e a escolha correta. Em ambientes de desenvolvimento local onde o banco ja existe, DatabaseTransactions pode ser mais rapido.

Por que usar postJson em vez de post?

O postJson configura automaticamente os headers HTTP para JSON, simulando o comportamento real de um cliente API. Alem disso, ele serializa o array de dados como JSON no corpo da requisicao. Sem esses headers, o Laravel pode interpretar os dados como form-urlencoded, produzindo comportamentos diferentes do esperado em producao.

Como testar jobs e eventos sem executa-los de verdade?

Usando os metodos fake() das facades correspondentes (Queue::fake(), Event::fake(), Bus::fake()). Apos a execucao da acao, os metodos assertPushed, assertDispatched e assertNotDispatched verificam que os efeitos colaterais ocorreram com os parametros corretos, sem processar nada de verdade.

O que sao testes de arquitetura e por que importam?

Testes de arquitetura validam a estrutura do codigo, nao o comportamento. Eles garantem que convencoes como separacao de camadas, ausencia de funcoes de debug e encapsulamento de classes sejam respeitadas automaticamente. Sao executados em milissegundos e previnem regressoes arquiteturais que code reviews manuais podem deixar passar.

Quando usar Mockery vs facades fake?

Facades fake sao ideais para servicos nativos do Laravel como Mail, Queue, Storage e Notification. Mockery e necessario quando a dependencia nao e uma facade, como clientes HTTP customizados, gateways de pagamento ou qualquer servico registrado no container que nao oferece um metodo fake() nativo.

Conclusao

Testes automatizados no Laravel com Pest deixaram de ser um diferencial competitivo e se tornaram prerequisito em processos seletivos para desenvolvedores PHP em 2026. O ecossistema oferece ferramentas maduras para cada camada de validacao, desde testes unitarios isolados ate testes de arquitetura e mutation testing.

Os pontos essenciais que todo desenvolvedor Laravel deve dominar antes de uma entrevista tecnica incluem:

  • Testes de feature com requisicoes HTTP reais validam o contrato da API, a estrutura das respostas e os efeitos colaterais no banco de dados em um unico fluxo
  • Testes unitarios isolam logica de negocio pura, verificando calculos, validacoes e tratamento de excecoes sem dependencias do framework
  • Mocking de facades com Mail::fake(), Queue::fake() e similares permite verificar efeitos colaterais sem executa-los, isolando o comportamento sob teste
  • Mocking com Mockery e indispensavel para dependencias que nao sao facades, como clientes de APIs externas e gateways de pagamento
  • Testes de arquitetura protegem convencoes do projeto automaticamente, impedindo que controllers acessem o Eloquent diretamente ou que funcoes de debug cheguem a producao
  • Mutation testing mede a qualidade real da suite de testes, identificando assercoes fracas que passam mesmo quando o codigo esta incorreto
  • Factories e states do Eloquent permitem criar cenarios de teste expressivos e reutilizaveis, reduzindo duplicacao e aumentando a legibilidade

A preparacao para entrevistas Laravel nao se resume a decorar a API do Pest. Envolve a capacidade de justificar decisoes, escolher a ferramenta certa para cada cenario e demonstrar que testes automatizados sao parte integral do processo de desenvolvimento, nao uma etapa opcional executada apenas quando sobra tempo.

Tags

#laravel
#testing
#pest
#php
#interview

Compartilhar

Artigos relacionados