laravel-testing

fusengine's avatarfrom fusengine

Write tests with Pest/PHPUnit, feature tests, unit tests, mocking, and factories. Use when testing controllers, services, models, or implementing TDD.

⭐0stars🔀0forks📁View on GitHub🕐Updated Jan 10, 2026

When & Why to Use This Skill

This Claude skill streamlines the development of robust Laravel applications by automating the creation of comprehensive test suites using Pest and PHPUnit. It facilitates high-quality software delivery through automated unit and feature testing, sophisticated mocking techniques, and efficient database factory management, making it an essential companion for Test-Driven Development (TDD).

Use Cases

  • Generating Pest feature tests for Laravel controllers to verify API endpoints, status codes, and JSON response structures.
  • Creating isolated unit tests for complex service layer logic using Mockery to simulate database repositories or external service dependencies.
  • Defining advanced database factories and states to generate realistic mock data for testing complex model relationships.
  • Implementing Test-Driven Development (TDD) by quickly scaffolding test cases for new features before writing the underlying business logic.
  • Mocking external HTTP requests and third-party integrations to ensure reliable and fast test execution without network dependencies.
  • Validating application security by writing automated tests for authentication, authorization, and form validation constraints.
namelaravel-testing
descriptionWrite tests with Pest/PHPUnit, feature tests, unit tests, mocking, and factories. Use when testing controllers, services, models, or implementing TDD.

Laravel Testing

Documentation

Testing

Code Quality

Pest Feature Test

<?php

declare(strict_types=1);

use App\Models\Post;
use App\Models\User;

describe('PostController', function () {
    beforeEach(function () {
        $this->user = User::factory()->create();
    });

    it('lists all posts', function () {
        Post::factory()->count(3)->create();

        $this->getJson('/api/v1/posts')
            ->assertOk()
            ->assertJsonCount(3, 'data');
    });

    it('creates a post when authenticated', function () {
        $data = ['title' => 'Test', 'content' => 'Content', 'status' => 'draft'];

        $this->actingAs($this->user)
            ->postJson('/api/v1/posts', $data)
            ->assertCreated()
            ->assertJsonPath('data.title', 'Test');

        $this->assertDatabaseHas('posts', ['title' => 'Test']);
    });

    it('returns 401 for unauthenticated users', function () {
        $this->postJson('/api/v1/posts', [])
            ->assertUnauthorized();
    });

    it('validates required fields', function () {
        $this->actingAs($this->user)
            ->postJson('/api/v1/posts', [])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['title', 'content']);
    });
});

Unit Test with Mocking

<?php

declare(strict_types=1);

use App\Services\PostService;
use App\Repositories\Contracts\PostRepositoryInterface;

describe('PostService', function () {
    it('creates a post with valid data', function () {
        $repository = Mockery::mock(PostRepositoryInterface::class);
        $repository->shouldReceive('create')
            ->once()
            ->andReturn(new Post(['title' => 'Test']));

        $service = new PostService($repository);
        $post = $service->create(['title' => 'Test']);

        expect($post->title)->toBe('Test');
    });
});

Factory

<?php

declare(strict_types=1);

namespace Database\Factories;

final class PostFactory extends Factory
{
    public function definition(): array
    {
        return [
            'title' => fake()->sentence(),
            'content' => fake()->paragraphs(3, true),
            'status' => PostStatus::Draft,
            'user_id' => User::factory(),
        ];
    }

    public function published(): static
    {
        return $this->state([
            'status' => PostStatus::Published,
            'published_at' => now(),
        ]);
    }
}

Assertions

// Response
$response->assertOk();           // 200
$response->assertCreated();      // 201
$response->assertUnauthorized(); // 401
$response->assertUnprocessable(); // 422

// JSON
$response->assertJson(['key' => 'value']);
$response->assertJsonPath('data.id', 1);
$response->assertJsonCount(3, 'data');

// Database
$this->assertDatabaseHas('posts', ['title' => 'Test']);
$this->assertDatabaseMissing('posts', ['title' => 'Deleted']);
$this->assertSoftDeleted('posts', ['id' => 1]);

Mocking

// Mock HTTP
Http::fake([
    'api.example.com/*' => Http::response(['data' => 'test'], 200),
]);

// Mock service
$this->mock(PaymentService::class, function ($mock) {
    $mock->shouldReceive('charge')->once()->andReturn(true);
});