SyntaxStudy
Sign Up
REST API Automated API Testing with PHPUnit and Laravel
REST API Beginner 1 min read

Automated API Testing with PHPUnit and Laravel

Laravel's testing utilities wrap PHPUnit with HTTP-level helpers that let you make requests to your application without a real HTTP server, inspect responses, and assert on the database state—all within a single test method. Tests run against a dedicated test database (using RefreshDatabase or a SQLite in-memory database) to stay isolated and fast. Feature tests are the most valuable layer for REST APIs: they test the full request-response cycle including middleware, validation, authorization, controller logic, and serialization. Unit tests are reserved for complex business logic, transformers, and value objects that can be tested independently.
Example
<?php
// tests/Feature/ProductApiTest.php
namespace Tests\Feature;

use App\Models\Product;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class ProductApiTest extends TestCase
{
    use RefreshDatabase;

    private User $admin;

    protected function setUp(): void
    {
        parent::setUp();
        $this->admin = User::factory()->create(['role' => 'ADMIN']);
    }

    public function test_guest_cannot_create_product(): void
    {
        $response = $this->postJson('/api/v1/products', [
            'name' => 'Test', 'price' => 9.99,
        ]);
        $response->assertStatus(401);
    }

    public function test_admin_can_create_product(): void
    {
        $response = $this->actingAs($this->admin, 'sanctum')
            ->postJson('/api/v1/products', [
                'name'       => 'Running Shoes',
                'price'      => 89.99,
                'categoryId' => 1,
            ]);
        $response->assertStatus(201)
                 ->assertJsonStructure(['id', 'name', 'price', 'createdAt'])
                 ->assertJson(['name' => 'Running Shoes', 'price' => 89.99]);
        $this->assertDatabaseHas('products', ['name' => 'Running Shoes']);
    }

    public function test_list_products_is_paginated(): void
    {
        Product::factory()->count(25)->create();
        $response = $this->actingAs($this->admin, 'sanctum')
            ->getJson('/api/v1/products?perPage=10');
        $response->assertOk()
                 ->assertJsonCount(10, 'data')
                 ->assertJsonPath('meta.total', 25)
                 ->assertJsonPath('meta.lastPage', 3);
    }
}