Laravel cookbook · Beginner

Write feature tests with fakes

Tests hit real queues and mail servers, or only test happy paths.

Create a free account to save this page, take notes and track your progress. Sign in

Approach

Use RefreshDatabase, model factories, actingAs() and fakes for queues, mail and HTTP. Assert on status codes, JSON, database state and dispatched jobs, including authorization failures.

Solution

<?php
// tests/Feature/PlaceOrderTest.php
namespace Tests\Feature;

use App\Jobs\SyncInvoiceToAccounting;
use App\Models\Product;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Queue;
use Tests\TestCase;

class PlaceOrderTest extends TestCase
{
    use RefreshDatabase;

    public function test_customer_can_place_an_order(): void
    {
        Queue::fake();
        $user = User::factory()->create();
        $product = Product::factory()->create(['stock' => 3, 'price_cents' => 2400]);

        $this->actingAs($user)
            ->postJson('/api/orders', ['product_id' => $product->id, 'quantity' => 2, 'shipping' => ['country' => 'DE']])
            ->assertCreated()
            ->assertJsonPath('data.total', 48);

        $this->assertDatabaseHas('products', ['id' => $product->id, 'stock' => 1]);
        Queue::assertPushed(SyncInvoiceToAccounting::class);
    }

    public function test_quantity_is_validated(): void
    {
        $this->actingAs(User::factory()->create())
            ->postJson('/api/orders', ['product_id' => 999, 'quantity' => 50])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['product_id', 'quantity', 'shipping.country']);
    }

    public function test_guests_cannot_order(): void
    {
        $this->postJson('/api/orders', [])->assertUnauthorized();
    }
}

Pitfalls

  • Faking everything, including the database, so tests pass while queries fail in production.
  • Only testing as an admin; test as regular users and other tenants.
  • Factories creating huge object graphs by default; keep them minimal with states.

Variations

  • Pest offers the same assertions with a terser syntax (it('places an order', function () { ... })).
  • Run php artisan test --parallel in CI.

Further reading: HTTP tests

Reference: https://laravel.com/docs/13.x/http-tests

More Laravel recipes

Was this page helpful?