Laravel REST API Testing and Documentation Workflow

#laravel #rest api #api testing #api documentation #pest #phpunit

An untested Laravel API is a promise with no guarantees. A poorly documented one is a promise only the original developer can read. Most API quality problems I encounter in existing projects come from one of two situations: either the API has no automated tests so small changes break things without anyone noticing, or the documentation is so outdated that the mobile developers maintain their own notes about what the API actually does rather than what the documentation claims it does.

Testing and documentation are not afterthoughts in a professional API workflow. They are structural decisions that shape how the API is maintained over time.

The Case for Feature Tests Over Unit Tests in API Development

Unit tests verify isolated functions. Feature tests verify the behavior of the full HTTP request and response cycle. For a REST API, feature tests are more valuable because they test what the API consumer actually experiences: send this request, get this response.

In Laravel, feature tests use the HTTP testing helpers to make requests against the application without a real HTTP server. A feature test for a resource creation endpoint might look like this:

  • Authenticate as a test user with the required token abilities
  • Send a POST request to the endpoint with valid data
  • Assert the response status is 201
  • Assert the response JSON contains the expected structure
  • Assert the database record was created with the correct values

This single test verifies authentication, routing, controller logic, validation, response structure, and database persistence in one pass. A unit test of just the controller method would miss every layer around it.

Test Coverage That Actually Matters

Full code coverage is a misleading target. A more useful target for a REST API is behavior coverage: every endpoint tested for its success case, its common failure cases, and its authorization boundaries.

For each API endpoint, the minimum test set should include:

  • Happy path: Valid request by an authorized user returns the expected response
  • Authentication failure: Unauthenticated request returns 401
  • Authorization failure: Authenticated user without the right permission returns 403
  • Validation failures: Missing required fields return 422 with the expected error structure
  • Not found: Request for a non-existent resource returns 404
  • Object-level authorization: A user cannot access another user's resources

This set of six tests per endpoint provides meaningful coverage without testing every conceivable edge case. For endpoints that handle money, billing, or irreversible operations, additional tests for specific edge cases are warranted.

Using Pest for Readable API Tests

Pest is a testing framework built on top of PHPUnit that many Laravel teams now prefer for its expressive, readable syntax. An API test in Pest reads almost like a specification:

it('returns order details for the authenticated owner', function () {
    $user = User::factory()->create();
    $order = Order::factory()->for($user)->create();

    actingAs($user)
        ->getJson("/api/v1/orders/{$order->id}")
        ->assertOk()
        ->assertJsonStructure([
            'data' => ['id', 'status', 'total', 'created_at']
        ]);
});

it('returns 403 when a user requests another user\'s order', function () {
    $owner = User::factory()->create();
    $requester = User::factory()->create();
    $order = Order::factory()->for($owner)->create();

    actingAs($requester)
        ->getJson("/api/v1/orders/{$order->id}")
        ->assertForbidden();
});

These tests are readable by non-PHP developers. A mobile developer reviewing the test suite can understand what the API is supposed to do without needing to parse controller code. That readability is a team communication benefit, not just a developer preference.

Testing with Factories: Realistic Test Data

Laravel model factories generate realistic test data for tests without requiring a seeded database. Well-designed factories produce data that reflects real-world variety: different user roles, different record states, edge cases in data content.

For API testing specifically, factories should cover the different states a resource can be in. An order can be pending, paid, shipped, or cancelled. Tests should exercise all of these states where the endpoint behavior differs. A shipped order might not be cancellable. A paid order might not be editable. These state-dependent behaviors need to be tested explicitly.

Use database transactions in tests rather than truncating tables between test runs. Laravel's RefreshDatabase trait wraps each test in a transaction that is rolled back at the end, ensuring a clean state without the performance cost of re-running migrations for every test.

API Documentation: Living, Not Static

Documentation written once and never updated is worse than no documentation because it actively misleads developers. The goal of API documentation in a professional workflow is documentation that stays accurate as the API evolves.

There are two practical approaches to this. The first is annotation-based documentation using tools like Scribe or Swagger-PHP. You annotate your controllers and form request classes with descriptions, example values, and response schemas. The documentation tool reads these annotations and generates formatted API reference documentation. When you change an endpoint, you update the annotation alongside the code change.

The second approach is test-driven documentation. Tools like Scribe can generate API documentation by running your test suite and recording the actual requests and responses. The documentation is derived from real API behavior rather than manually written descriptions, which means it cannot describe functionality that does not actually work.

Versioning Documentation Alongside Code

If your API uses route versioning, your documentation must mirror that versioning. /api/v1/orders and /api/v2/orders may have different request structures and different response shapes. A documentation page that does not distinguish between versions will confuse every developer who reads it.

The most sustainable approach is to keep documentation in source control alongside the code. When a new API version is introduced, the documentation for that version is created alongside the route and controller code. When the old version is deprecated, the documentation marks it as deprecated with the deprecation date and a migration guide to the new version.

Postman Collections for Integration Testing

Even with a comprehensive automated test suite, having a maintained Postman collection is valuable for developer onboarding and for manual exploration of API behavior. A Postman collection for a REST API should cover every endpoint with example requests and, ideally, collection-level scripts that handle authentication automatically so that a new developer can import the collection and start making real API requests within minutes.

Export the Postman collection as a JSON file and commit it to the repository. Update it when endpoints change. A stale Postman collection, like stale documentation, is a source of confusion rather than a productivity tool.

If you are building a Laravel REST API and want a testing and documentation strategy that your team can maintain as the project grows, visit my Laravel API development services page to learn how I structure test coverage and documentation in production API projects.


Prakash Tank

Prakash Tank

Full-Stack Architect & Tech Enthusiast. Passionate about building scalable applications and sharing knowledge with the community.