PHP

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

8 min de leitura

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

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

<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">&lt;?php

namespace Tests\Unit;

use PHPUnit\Framework\TestCase;

class CalculadoraTest extends TestCase

{

public function testSomaDoisNumeros()

{

$resultado = 2 + 2;

$this-&gt;assertEquals(4, $resultado);

}

public function testDivisaoPorZeroLancaExcecao()

{

$this-&gt;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-&gt;get()</code>, <code>$this-&gt;post()</code> e <code>$this-&gt;json()</code> para fazer requisições HTTP simuladas. Aqui está um exemplo realista:</p>

<pre><code class="language-php">&lt;?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()-&gt;count(3)-&gt;create();

$response = $this-&gt;get(&#039;/api/users&#039;);

$response-&gt;assertStatus(200);

$response-&gt;assertJsonCount(3);

}

public function testCriaUsuarioComDadosValidos()

{

$dados = [

&#039;name&#039; =&gt; &#039;João Silva&#039;,

&#039;email&#039; =&gt; &#039;joao@example.com&#039;,

&#039;password&#039; =&gt; &#039;senha123456&#039;,

];

$response = $this-&gt;post(&#039;/api/users&#039;, $dados);

$response-&gt;assertStatus(201);

$this-&gt;assertDatabaseHas(&#039;users&#039;, [&#039;email&#039; =&gt; &#039;joao@example.com&#039;]);

}

public function testRejeitaDadosInvalidos()

{

$response = $this-&gt;post(&#039;/api/users&#039;, [&#039;name&#039; =&gt; &#039;João&#039;]);

$response-&gt;assertStatus(422);

$response-&gt;assertJsonValidationErrors([&#039;email&#039;, &#039;password&#039;]);

}

}</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">&lt;?php

use App\Models\Post;

use Tests\TestCase;

uses(TestCase::class)-&gt;in(&#039;Feature&#039;);

test(&#039;usuário autenticado pode criar post&#039;, function () {

$user = User::factory()-&gt;create();

$response = $this-&gt;actingAs($user)

-&gt;post(&#039;/api/posts&#039;, [

&#039;title&#039; =&gt; &#039;Meu Post&#039;,

&#039;content&#039; =&gt; &#039;Conteúdo do post&#039;,

]);

$response-&gt;assertStatus(201);

expect(Post::count())-&gt;toBe(1);

});

it(&#039;rejeita posts com título vazio&#039;, function () {

$user = User::factory()-&gt;create();

$response = $this-&gt;actingAs($user)

-&gt;post(&#039;/api/posts&#039;, [&#039;content&#039; =&gt; &#039;Conteúdo&#039;]);

$response-&gt;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)-&gt;toBeInt()-&gt;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">&lt;?php

beforeEach(function () {

$this-&gt;user = User::factory()-&gt;create();

$this-&gt;admin = User::factory()-&gt;admin()-&gt;create();

});

test(&#039;admin pode deletar qualquer post&#039;, function () {

$post = Post::factory()-&gt;create();

$response = $this-&gt;actingAs($this-&gt;admin)

-&gt;delete(&quot;/api/posts/{$post-&gt;id}&quot;);

$response-&gt;assertStatus(204);

expect(Post::count())-&gt;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 &quot;o que&quot; não no &quot;como&quot;; 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-&gt;assertEquals($esperado, $resultado);

// vs Pest

expect($resultado)-&gt;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>

Comentários

Mais em PHP

Dominando Transações em PDO: Garantindo Integridade dos Dados em Projetos Reais
Dominando Transações em PDO: Garantindo Integridade dos Dados em Projetos Reais

O que são Transações em PDO? Uma transação é um conjunto de operações no banc...

Como Usar Autenticação JWT e OAuth 2.0 em APIs PHP em Produção
Como Usar Autenticação JWT e OAuth 2.0 em APIs PHP em Produção

JWT: Fundamentos e Implementação Prática JWT (JSON Web Token) é um padrão abe...

Camada Controller: Recebendo Requisições e Orquestrando Respostas na Prática
Camada Controller: Recebendo Requisições e Orquestrando Respostas na Prática

O Papel do Controller na Arquitetura MVC O controller é o intermediário funda...