laravel-testing

Laravel 12 testing with Pest PHP 4 or PHPUnit 11. 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.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "laravel-testing" with this command: npx skills add asyrafhussin/agent-skills/asyrafhussin-agent-skills-laravel-testing

Laravel 12 Testing — Pest PHP 4 & PHPUnit 11

Supports both Pest PHP 4 and PHPUnit 11. See Framework Detection below.

PHPUnit version note: Laravel 12 ships with phpunit/phpunit: ^11.5.50 in its default composer.json. PHPUnit 12 and 13 exist but are not the Laravel 12 default — to use them, manually update the constraint. All patterns in this skill are compatible with PHPUnit 11, 12, and 13.

Comprehensive testing guide for Laravel 12 applications. Contains 21 rules across 6 categories for writing fast, readable, and reliable tests. Supports both Pest PHP 4 and PHPUnit 11 (Laravel 12 default).

Framework Detection

Before writing or reviewing any test code, detect which testing framework the project uses:

Step 1 — Check composer.json

# Look for these in require-dev:
# "pestphp/pest" → Pest
# "phpunit/phpunit" (without pest) → PHPUnit
  • If pestphp/pest is present → use Pest syntax
  • If only phpunit/phpunit is present → use PHPUnit syntax
  • If both are present → Pest takes priority (Pest runs on top of PHPUnit)

Step 2 — Check for tests/Pest.php

  • If tests/Pest.php exists → Pest is configured, use Pest syntax

Step 3 — If still unclear, ask the user

"I couldn't detect the testing framework. Does this project use Pest PHP or PHPUnit?"


Syntax Reference

Test Declaration

PestPHPUnit
Test functiontest('...', fn() => ...)public function test_...(): void
Readable nameit('...', fn() => ...)/** @test */ public function it_...()
Groupingdescribe('...', fn() => ...)Test class name / nested classes
Trait applicationuses(RefreshDatabase::class)use RefreshDatabase; inside class
Before eachbeforeEach(fn() => ...)protected function setUp(): void
After eachafterEach(fn() => ...)protected function tearDown(): void
Parameterised->with([...])@dataProvider method
Global setupuses(...)->in('Feature') in Pest.phpBase TestCase class

Core assertions (identical in both frameworks)

assertStatus, assertJson, assertJsonPath, assertDatabaseHas, assertModelExists, actingAs, Mail::fake(), Queue::fake(), Event::fake(), Notification::fake(), Storage::fake() — all work the same in Pest and PHPUnit.


When to Apply

Reference these guidelines when:

  • Writing feature or unit tests for Laravel
  • Testing HTTP endpoints and API responses
  • Creating factories and test data
  • Asserting database state after operations
  • Faking Mail, Queue, Notification, or Event facades
  • Testing authenticated routes and API tokens
  • Organising tests with describe blocks, datasets, or test classes

Rule Categories by Priority

PriorityCategoryImpactPrefix
1HTTP & Feature TestsCRITICALhttp-
2Model FactoriesCRITICALfactory-
3Database AssertionsHIGHdb-
4Faking ServicesHIGHfake-
5Authentication TestingHIGHauth-
6Test Organisation PatternsMEDIUMpest-

Quick Reference

1. HTTP & Feature Tests (CRITICAL)

  • http-test-structure - Arrange/Act/Assert with factories — Pest + PHPUnit examples
  • http-assert-response - assertStatus, assertJson, assertRedirect, assertJsonMissing
  • http-assert-json-fluent - Fluent assertJson with AssertableJson closure
  • http-refresh-database - RefreshDatabase vs DatabaseTransactions — when to use each

2. Model Factories (CRITICAL)

  • factory-define - Define factories with typed fake data and PHP 8.3 syntax
  • factory-states - Factory states for distinct test scenarios
  • factory-sequences - sequence() for varied data across multiple records
  • factory-relationships - has(), for(), recycle(), afterCreating()

3. Database Assertions (HIGH)

  • db-assert-has - assertDatabaseHas, assertModelExists for presence checks
  • db-assert-missing - assertDatabaseMissing, assertModelMissing for deletion
  • db-assert-soft-deletes - assertSoftDeleted, trashed() factory state

4. Faking Services (HIGH)

  • fake-mail - Mail::fake(), assertSent vs assertQueued, assertNothingSent
  • fake-queue - Queue::fake(), assertPushed, assertPushedOn
  • fake-notification - Notification::fake(), assertSentTo, assertCount
  • fake-event - Event::fake(), assertDispatched, assertNotDispatched
  • fake-storage - Storage::fake(), UploadedFile::fake(), assertExists

5. Authentication Testing (HIGH)

  • auth-acting-as - actingAs() for session/web authenticated tests
  • auth-sanctum - Sanctum::actingAs() for API token authentication

6. Test Organisation Patterns (MEDIUM)

  • pest-describe-it - describe()/it() (Pest) or test class organisation (PHPUnit)
  • pest-datasets - with() datasets (Pest) or @dataProvider (PHPUnit)
  • pest-hooks - beforeEach/afterEach (Pest) or setUp/tearDown (PHPUnit)

Essential Patterns

Pest

<?php

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

uses(RefreshDatabase::class);

test('authenticated user can create a post', function () {
    $user = User::factory()->create();

    $this->actingAs($user)
        ->postJson('/api/posts', ['title' => 'Hello World', 'body' => 'Content.'])
        ->assertStatus(201)
        ->assertJsonPath('data.title', 'Hello World');

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

PHPUnit

<?php

namespace Tests\Feature;

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

class PostControllerTest extends TestCase
{
    use RefreshDatabase;

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

        $this->actingAs($user)
            ->postJson('/api/posts', ['title' => 'Hello World', 'body' => 'Content.'])
            ->assertStatus(201)
            ->assertJsonPath('data.title', 'Hello World');

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

How to Use

Read individual rule files for detailed explanations and code examples.

Each rule file contains:

  • YAML frontmatter with metadata (title, impact, tags)
  • Brief explanation of why it matters
  • Bad Example with explanation
  • Good Example with both Pest and PHPUnit where syntax differs
  • Laravel 12 specific context and references

Full Compiled Document

For the complete guide with all rules expanded: AGENTS.md

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

clean-code-principles

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

typescript-react-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

php-best-practices

No summary provided by upstream source.

Repository SourceNeeds Review
Automation

react-vite-best-practices

No summary provided by upstream source.

Repository SourceNeeds Review