Master feature testing in Laravel. Learn to set up your environment, write tests for API endpoints, and assert JSON responses to build rock-solid applications.
Previously in this course, we explored asynchronous processing with queues in Laravel to offload heavy tasks. While background jobs improve performance, they also introduce complexity that requires automated verification. This lesson shifts our focus to feature testing, ensuring that our API endpoints behave exactly as expected before we ship code to production.
If you haven't already, you might want to review our introduction to testing to understand the philosophy behind testing in Laravel.
While unit tests focus on isolated methods or classes, feature tests cover the entire request-response lifecycle. When we test an API endpoint, we aren't just testing the controller method; we are testing the route, the middleware, the form request validation, the service layer, and the final JSON response.
In a professional Laravel environment, feature tests are your primary safety net. They allow you to refactor code—like moving logic into a service class—with the confidence that you haven't broken the user-facing API.
Laravel ships with Pest as the default testing framework, which sits on top of PHPUnit. To get started, ensure your phpunit.xml file exists in the root of your project. If you are using a modern Laravel installation, this is already configured.
To create your first feature test, run:
Bashphp artisan make:test TaskApiTest --pest
This generates a file in tests/Feature/TaskApiTest.php. Using the --pest flag gives us a cleaner, more readable syntax compared to traditional PHPUnit classes.
Let's test our project board's ability to fetch a list of tasks. A good feature test follows the "Arrange, Act, Assert" pattern.
PHP#6A9955">// tests/Feature/TaskApiTest.php use App\Models\User; use App\Models\Task; test('authenticated user can view their tasks', function () { #6A9955">// 1. Arrange: Create a user and some tasks $user = User::factory()->create(); Task::factory()->count(3)->for($user)->create(); #6A9955">// 2. Act: Send a GET request to the index endpoint $response = $this->actingAs($user, 'sanctum') ->getJson('/api/v1/tasks'); #6A9955">// 3. Assert: Verify the response $response->assertStatus(200) ->assertJsonCount(3, 'data') ->assertJsonStructure([ 'data' => [ '*' => ['id', 'title', 'status'] ] ]); });
actingAs($user, 'sanctum'): This helper authenticates the request as if a user provided a valid Sanctum token.getJson(...): This sends an HTTP GET request and expects a JSON response, setting the Accept: application/json header automatically.assertJsonCount(3, 'data'): Since our API uses resources, it typically returns data nested under a data key. This ensures the count is correct.assertJsonStructure(...): This validates the shape of your JSON output without forcing you to hardcode every specific value, which is useful for dynamic data like timestamps.Now it's your turn. In our project board, we have a POST /api/v1/tasks endpoint. Create a test file named CreateTaskTest.php and write a test that:
['title' => 'New Task']).201 Created.assertDatabaseHas('tasks', ['title' => 'New Task']).Testing is powerful, but it’s easy to fall into traps that make your suite fragile:
assertJson(['id' => 1, ...]) for large objects. It makes tests brittle because they break whenever a unrelated field changes. Use assertJsonStructure or assertJsonPath instead.Illuminate\Foundation\Testing\RefreshDatabase trait. This ensures your database is migrated and cleared between every single test, preventing "leaky" state where one test affects the outcome of another.Feature tests are the cornerstone of a maintainable API. By simulating real HTTP requests, we ensure our middleware, validation, and business logic work in concert. We've covered:
actingAs to simulate authentication.RefreshDatabase for clean test states.Automated testing is not an optional "extra"—it is the only way to scale a production application without constant manual regressions.
Up next: We will dive into Mocking Services and Repositories in Tests, where we'll learn how to isolate our controllers from complex external dependencies to make our test suite run lightning fast.
Master Testing DDD components in Laravel. Learn to mock external services, isolate domain logic, and write reliable PHPUnit tests for your Action classes.
Read moreStop hitting live APIs in your tests. Learn how to use test doubles, stubs, and mocks to isolate your Laravel application logic for faster, reliable tests.
Feature Testing Fundamentals