All skills
asyrafhussin avatar

/laravel-testing

@a5fe20e

Laravel 13 testing with Pest PHP 4 or PHPUnit 12. Use when writing feature tests, unit tests, or any test code in a Laravel application. Triggers on tasks involving HTTP tests, model factories, database assertions, mocking facades, authentication testing, or test organisation patterns.

Use this Skill: https://skilld.dev/gh/asyrafhussin/agent-skills/laravel-testing

This session only. Nothing lands on disk.

ruleshttp-test-structure.md

≈1.1k tokens on demand. Your agent reads this file only when SKILL.md points to it.

Feature Test Structure with Arrange/Act/Assert

Impact: CRITICAL (Produces readable, maintainable, and deterministic tests)

Structure every feature test with three clear phases: Arrange (set up state with factories), Act (make the HTTP request), Assert (verify the response and side effects). Test behavior and outcomes — not implementation details.

Bad Example

<?php

// No factory — raw DB insert is fragile and bypasses model logic
test('create post', function () {
    DB::table('users')->insert(['name' => 'Alice', 'email' => 'a@a.com', 'password' => 'pass']);
    DB::table('posts')->insert(['title' => 'Test', 'user_id' => 1]);

    // Asserts internal implementation detail (method was called)
    $mock = Mockery::mock(PostRepository::class);
    $mock->shouldReceive('save')->once();
    app()->instance(PostRepository::class, $mock);

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

Good Example — Pest

<?php

use App\Models\User;
use App\Models\Post;
use Illuminate\Foundation\Testing\RefreshDatabase;

uses(RefreshDatabase::class);

test('authenticated user can create a post', function () {
    // Arrange — factories handle all setup
    $user = User::factory()->create();

    // Act — one HTTP call
    $response = $this->actingAs($user)
        ->postJson('/api/posts', [
            'title' => 'Hello World',
            'body'  => 'Some content.',
        ]);

    // Assert — behavior and outcomes, not internals
    $response->assertStatus(201)
        ->assertJsonPath('data.title', 'Hello World');

    $this->assertDatabaseHas('posts', [
        'title'   => 'Hello World',
        'user_id' => $user->id,
    ]);
});

Good Example — PHPUnit

<?php

namespace Tests\Feature;

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

class PostControllerTest extends TestCase
{
    use RefreshDatabase;

    public function test_authenticated_user_can_create_a_post(): void
    {
        // Arrange
        $user = User::factory()->create();

        // Act
        $response = $this->actingAs($user)
            ->postJson('/api/posts', [
                'title' => 'Hello World',
                'body'  => 'Some content.',
            ]);

        // Assert
        $response->assertStatus(201)
            ->assertJsonPath('data.title', 'Hello World');

        $this->assertDatabaseHas('posts', [
            'title'   => 'Hello World',
            'user_id' => $user->id,
        ]);
    }
}

Testing a policy-based action (Pest):

<?php

uses(RefreshDatabase::class);

test('user cannot delete another users post', function () {
    $owner = User::factory()->create();
    $other = User::factory()->create();
    $post  = Post::factory()->for($owner)->create();

    $this->actingAs($other)
        ->deleteJson("/api/posts/{$post->id}")
        ->assertForbidden();

    $this->assertModelExists($post);
});

Testing a policy-based action (PHPUnit):

<?php

namespace Tests\Feature;

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

class PostPolicyTest extends TestCase
{
    use RefreshDatabase;

    public function test_user_cannot_delete_another_users_post(): void
    {
        $owner = User::factory()->create();
        $other = User::factory()->create();
        $post  = Post::factory()->for($owner)->create();

        $this->actingAs($other)
            ->deleteJson("/api/posts/{$post->id}")
            ->assertForbidden();

        $this->assertModelExists($post);
    }
}

Why It Matters

  • Readability: Clear phases make intent obvious to every team member
  • Deterministic: Factories + RefreshDatabase eliminate test pollution
  • Behavioral focus: Testing outcomes rather than internals means refactoring is safe
  • Maintainability: Changing implementation never breaks tests that only verify behavior

Reference: Laravel HTTP Tests

Source: SKILL.md on GitHub

No alerts17d5 checks · Risk SAFE
  • Gen Agent Trust Hub17d

    This skill is a comprehensive testing guide for Laravel 13, covering both Pest PHP and PHPUnit frameworks. It provides clear rules and code examples for HTTP testing, model factories, database assertions, and mocking services. No security risks or malicious patterns were identified.

  • Socket17d

    No alerts

  • Snyk17d

    Risk: LOW · No issues

  • Runlayer6mo

    25 files scanned · No issues

  • ZeroLeaks5mo

    Score: 93/100 · 2 sections analyzed

Signed by skilld at a5fe20e. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub last month.

Steadyupdated 7 months ago
Other metadata
metadata
{
  "author": "Laravel Community",
  "version": "1.1.0",
  "laravelVersion": "13.x",
  "phpVersion": "8.3+",
  "pestVersion": "4.x",
  "phpunitVersion": "12.x"
}

README badge

README badge for asyrafhussin/agent-skills/laravel-testing