{"id":"9b18a703-66fc-4e6e-b568-dd8f605c5fff","shortId":"66RLeS","kind":"skill","title":"laravel-testing","tagline":"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 pat","description":"# Laravel 13 Testing — Pest PHP 4 & PHPUnit 12\n\n> **Supports both Pest PHP 4 and PHPUnit 12.** See Framework Detection below.\n>\n> **PHPUnit version note:** Laravel 13 ships with `phpunit/phpunit: ^12.5.12` in its default `composer.json`. All patterns in this skill are compatible with PHPUnit 11, 12, and 13.\n\nComprehensive testing guide for Laravel 13 applications. Contains 24 rules across 6 categories for writing fast, readable, and reliable tests. Supports both **Pest PHP 4** and **PHPUnit 12** (Laravel 13 default).\n\n## Framework Detection\n\n**Before writing or reviewing any test code, detect which testing framework the project uses:**\n\n### Step 1 — Check composer.json\n\n```bash\n# Look for these in require-dev:\n# \"pestphp/pest\" → Pest\n# \"phpunit/phpunit\" (without pest) → PHPUnit\n```\n\n- If `pestphp/pest` is present → **use Pest syntax**\n- If only `phpunit/phpunit` is present → **use PHPUnit syntax**\n- If both are present → **Pest takes priority** (Pest runs on top of PHPUnit)\n\n### Step 2 — Check for tests/Pest.php\n\n- If `tests/Pest.php` exists → **Pest is configured**, use Pest syntax\n\n### Step 3 — If still unclear, ask the user\n\n> \"I couldn't detect the testing framework. Does this project use **Pest PHP** or **PHPUnit**?\"\n\n---\n\n## Syntax Reference\n\n### Test Declaration\n\n| | Pest | PHPUnit |\n|--|------|---------|\n| Test function | `test('...', fn() => ...)` | `public function test_...(): void` |\n| Readable name | `it('...', fn() => ...)` | `#[Test] public function it_...()` |\n| Grouping | `describe('...', fn() => ...)` | Test class name / nested classes |\n| Trait application | `uses(RefreshDatabase::class)` | `use RefreshDatabase;` inside class |\n| Before each | `beforeEach(fn() => ...)` | `protected function setUp(): void` |\n| After each | `afterEach(fn() => ...)` | `protected function tearDown(): void` |\n| Parameterised | `->with([...])` | `#[DataProvider]` attribute |\n| Global setup | `uses(...)->in('Feature')` in `Pest.php` | Base `TestCase` class |\n\n### Core assertions (identical in both frameworks)\n\n`assertStatus`, `assertJson`, `assertJsonPath`, `assertDatabaseHas`, `assertModelExists`, `actingAs`, `Mail::fake()`, `Queue::fake()`, `Event::fake()`, `Notification::fake()`, `Storage::fake()` — all work the same in Pest and PHPUnit.\n\n---\n\n## When to Apply\n\nReference these guidelines when:\n- Writing feature or unit tests for Laravel\n- Testing HTTP endpoints and API responses\n- Creating factories and test data\n- Asserting database state after operations\n- Faking Mail, Queue, Notification, or Event facades\n- Testing authenticated routes and API tokens\n- Organising tests with describe blocks, datasets, or test classes\n\n## Rule Categories by Priority\n\n| Priority | Category | Impact | Prefix |\n|----------|----------|--------|--------|\n| 1 | HTTP & Feature Tests | CRITICAL | `http-` |\n| 2 | Model Factories | CRITICAL | `factory-` |\n| 3 | Database Assertions | HIGH | `db-` |\n| 4 | Faking Services | HIGH | `fake-` |\n| 5 | Authentication Testing | HIGH | `auth-` |\n| 6 | Test Organisation Patterns | MEDIUM | `pest-` |\n\n## Quick Reference\n\n### 1. HTTP & Feature Tests (CRITICAL)\n\n- `http-test-structure` - Arrange/Act/Assert with factories — Pest + PHPUnit examples\n- `http-assert-response` - assertStatus, assertJson, assertRedirect, assertJsonMissing\n- `http-assert-json-fluent` - Fluent assertJson with AssertableJson closure\n- `http-refresh-database` - RefreshDatabase vs DatabaseTransactions — when to use each\n\n### 2. Model Factories (CRITICAL)\n\n- `factory-define` - Define factories with typed fake data and PHP 8.3 syntax\n- `factory-states` - Factory states for distinct test scenarios\n- `factory-sequences` - sequence() for varied data across multiple records\n- `factory-relationships` - has(), for(), recycle(), afterCreating()\n\n### 3. Database Assertions (HIGH)\n\n- `db-assert-has` - assertDatabaseHas, assertModelExists for presence checks\n- `db-assert-missing` - assertDatabaseMissing, assertModelMissing for deletion\n- `db-assert-soft-deletes` - assertSoftDeleted, trashed() factory state\n\n### 4. Faking Services (HIGH)\n\n- `fake-mail` - Mail::fake(), assertSent vs assertQueued, assertNothingSent\n- `fake-queue` - Queue::fake(), assertPushed, assertPushedOn\n- `fake-notification` - Notification::fake(), assertSentTo, assertCount\n- `fake-event` - Event::fake(), assertDispatched, assertNotDispatched\n- `fake-storage` - Storage::fake(), UploadedFile::fake(), assertExists\n- `fake-ai-agent` - Agent::fake(), assertPrompted, preventStrayPrompts (Laravel 13+)\n- `fake-ai-media` - Image::fake(), Audio::fake(), Transcription::fake() (Laravel 13+)\n- `fake-ai-data` - Embeddings::fake(), Reranking::fake(), Files::fake(), Stores::fake() (Laravel 13+)\n\n### 5. Authentication Testing (HIGH)\n\n- `auth-acting-as` - actingAs() for session/web authenticated tests\n- `auth-sanctum` - Sanctum::actingAs() for API token authentication\n\n### 6. Test Organisation Patterns (MEDIUM)\n\n- `pest-describe-it` - describe()/it() (Pest) or test class organisation (PHPUnit)\n- `pest-datasets` - with() datasets (Pest) or #[DataProvider] (PHPUnit)\n- `pest-hooks` - beforeEach/afterEach (Pest) or setUp/tearDown (PHPUnit)\n\n## Essential Patterns\n\n### Pest\n\n```php\n<?php\n\nuse App\\Models\\User;\nuse Illuminate\\Foundation\\Testing\\RefreshDatabase;\n\nuses(RefreshDatabase::class);\n\ntest('authenticated user can create a post', function () {\n    $user = User::factory()->create();\n\n    $this->actingAs($user)\n        ->postJson('/api/posts', ['title' => 'Hello World', 'body' => 'Content.'])\n        ->assertStatus(201)\n        ->assertJsonPath('data.title', 'Hello World');\n\n    $this->assertDatabaseHas('posts', ['title' => 'Hello World', 'user_id' => $user->id]);\n});\n```\n\n### PHPUnit\n\n```php\n<?php\n\nnamespace Tests\\Feature;\n\nuse App\\Models\\User;\nuse Illuminate\\Foundation\\Testing\\RefreshDatabase;\nuse Tests\\TestCase;\n\nclass PostControllerTest extends TestCase\n{\n    use RefreshDatabase;\n\n    public function test_authenticated_user_can_create_a_post(): void\n    {\n        $user = User::factory()->create();\n\n        $this->actingAs($user)\n            ->postJson('/api/posts', ['title' => 'Hello World', 'body' => 'Content.'])\n            ->assertStatus(201)\n            ->assertJsonPath('data.title', 'Hello World');\n\n        $this->assertDatabaseHas('posts', ['title' => 'Hello World', 'user_id' => $user->id]);\n    }\n}\n```\n\n## How to Use\n\nRead individual rule files for detailed explanations and code examples.\n\nEach rule file contains:\n- YAML frontmatter with metadata (title, impact, tags)\n- Brief explanation of why it matters\n- Bad Example with explanation\n- Good Example with both Pest and PHPUnit where syntax differs\n- Laravel 13 specific context and references\n\n## Full Compiled Document\n\nFor the complete guide with all rules expanded: `AGENTS.md`","tags":["laravel","testing","agent","skills","asyrafhussin","agent-rules","agent-skills","ai-agents","ai-slop","claude-code","code-quality","code-review"],"capabilities":["skill","source-asyrafhussin","skill-laravel-testing","topic-agent-rules","topic-agent-skills","topic-ai-agents","topic-ai-slop","topic-claude-code","topic-code-quality","topic-code-review","topic-codex","topic-cursor","topic-laravel","topic-nodejs","topic-react"],"categories":["agent-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/AsyrafHussin/agent-skills/laravel-testing","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add AsyrafHussin/agent-skills","source_repo":"https://github.com/AsyrafHussin/agent-skills","install_from":"skills.sh"}},"qualityScore":"0.469","qualityRationale":"deterministic score 0.47 from registry signals: · indexed on github topic:agent-skills · 39 github stars · SKILL.md body (6,782 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-05-18T18:58:24.959Z","embedding":null,"createdAt":"2026-05-16T18:57:14.590Z","updatedAt":"2026-05-18T18:58:24.959Z","lastSeenAt":"2026-05-18T18:58:24.959Z","tsv":"'/api/posts':700,764 '/it':643 '1':141,382,416 '11':89 '12':13,54,62,90,120 '12.5.12':75 '13':5,48,71,92,98,122,584,596,610,831 '2':187,388,460 '201':707,771 '24':101 '3':201,393,503 '4':10,52,59,117,398,533 '5':403,611 '6':104,408,633 '8.3':475 'across':103,493 'act':617 'actinga':303,619,628,697,761 'aftercr':502 'aftereach':272 'agent':578,579 'agents.md':847 'ai':577,587,599 'api':340,363,630 'app':673,729 'appli':324 'applic':28,99,254 'arrange/act/assert':425 'ask':205 'assert':38,293,347,395,433,441,505,509,518,526 'assertablejson':447 'assertcount':559 'assertdatabaseha':301,511,713,777 'assertdatabasemiss':520 'assertdispatch':565 'assertexist':574 'assertjson':299,436,445 'assertjsonmiss':438 'assertjsonpath':300,708,772 'assertmodelexist':302,512 'assertmodelmiss':521 'assertnotdispatch':566 'assertnothings':545 'assertprompt':581 'assertpush':551 'assertpushedon':552 'assertqueu':544 'assertredirect':437 'asserts':542 'assertsentto':558 'assertsoftdelet':529 'assertstatus':298,435,706,770 'attribut':281 'audio':591 'auth':407,616,625 'auth-acting-a':615 'auth-sanctum':624 'authent':41,360,404,612,622,632,685,749 'bad':816 'base':289 'bash':144 'beforeeach':264 'beforeeach/aftereach':662 'block':369 'bodi':704,768 'brief':810 'categori':105,375,379 'check':142,188,515 'class':249,252,257,261,291,373,647,683,740 'closur':448 'code':24,132,797 'compat':86 'compil':837 'complet':841 'composer.json':79,143 'comprehens':93 'configur':196 'contain':100,802 'content':705,769 'context':833 'core':292 'couldn':209 'creat':342,688,695,752,759 'critic':386,391,420,463 'data':346,472,492,600 'data.title':709,773 'databas':37,348,394,452,504 'databasetransact':455 'dataprovid':280,657 'dataset':370,652,654 'db':397,508,517,525 'db-assert-ha':507 'db-assert-miss':516 'db-assert-soft-delet':524 'declar':226 'default':78,123 'defin':466,467 'delet':523,528 'describ':246,368,640,642 'detail':794 'detect':65,125,133,211 'dev':151 'differ':829 'distinct':483 'document':838 'embed':601 'endpoint':338 'essenti':667 'event':308,357,562,563 'exampl':430,798,817,821 'exist':193 'expand':846 'explan':795,811,819 'extend':742 'facad':40,358 'factori':36,343,390,392,427,462,465,468,478,480,487,497,531,694,758 'factory-defin':464 'factory-relationship':496 'factory-sequ':486 'factory-st':477 'fake':305,307,309,311,313,352,399,402,471,534,538,541,547,550,554,557,561,564,568,571,573,576,580,586,590,592,594,598,602,604,606,608 'fake-ai-ag':575 'fake-ai-data':597 'fake-ai-media':585 'fake-ev':560 'fake-mail':537 'fake-notif':553 'fake-queu':546 'fake-storag':567 'fast':108 'featur':17,286,330,384,418,727 'file':605,792,801 'fluent':443,444 'fn':232,240,247,265,273 'foundat':678,734 'framework':64,124,136,214,297 'frontmatt':804 'full':836 'function':230,234,243,267,275,691,747 'global':282 'good':820 'group':245 'guid':95,842 'guidelin':327 'hello':702,710,716,766,774,780 'high':396,401,406,506,536,614 'hook':661 'http':33,337,383,387,417,422,432,440,450 'http-assert-json-flu':439 'http-assert-respons':431 'http-refresh-databas':449 'http-test-structur':421 'id':719,721,783,785 'ident':294 'illumin':677,733 'imag':589 'impact':380,808 'individu':790 'insid':260 'involv':32 'json':442 'laravel':2,4,27,47,70,97,121,335,583,595,609,830 'laravel-test':1 'look':145 'mail':304,353,539,540 'matter':815 'media':588 'medium':412,637 'metadata':806 'miss':519 'mock':39 'model':35,389,461,674,730 'multipl':494 'name':238,250 'namespac':725 'nest':251 'note':69 'notif':310,355,555,556 'oper':351 'organis':45,365,410,635,648 'parameteris':278 'pat':46 'pattern':81,411,636,668 'pest':8,50,57,115,153,156,163,177,180,194,198,219,227,319,413,428,639,644,651,655,660,663,669,824 'pest-dataset':650 'pest-describe-it':638 'pest-hook':659 'pest.php':288 'pestphp/pest':152,159 'php':9,51,58,116,220,474,670,671,723,724 'phpunit':12,53,61,67,88,119,157,171,185,222,228,321,429,649,658,666,722,826 'phpunit/phpunit':74,154,167 'post':690,714,754,778 'postcontrollertest':741 'postjson':699,763 'prefix':381 'presenc':514 'present':161,169,176 'preventstrayprompt':582 'prioriti':179,377,378 'project':138,217 'protect':266,274 'public':233,242,746 'queue':306,354,548,549 'quick':414 'read':789 'readabl':109,237 'record':495 'recycl':501 'refer':224,325,415,835 'refresh':451 'refreshdatabas':256,259,453,680,682,736,745 'relationship':498 'reliabl':111 'requir':150 'require-dev':149 'rerank':603 'respons':341,434 'review':129 'rout':361 'rule':102,374,791,800,845 'run':181 'sanctum':626,627 'scenario':485 'see':63 'sequenc':488,489 'servic':400,535 'session/web':621 'setup':268,283 'setup/teardown':665 'ship':72 'skill':84 'skill-laravel-testing' 'soft':527 'source-asyrafhussin' 'specif':832 'state':349,479,481,532 'step':140,186,200 'still':203 'storag':312,569,570 'store':607 'structur':424 'support':55,113 'syntax':164,172,199,223,476,828 'tag':809 'take':178 'task':31 'teardown':276 'test':3,6,18,20,23,34,42,44,49,94,112,131,135,213,225,229,231,235,241,248,333,336,345,359,366,372,385,405,409,419,423,484,613,623,634,646,679,684,726,735,738,748 'testcas':290,739,743 'tests/pest.php':190,192 'titl':701,715,765,779,807 'token':364,631 'top':183 'topic-agent-rules' 'topic-agent-skills' 'topic-ai-agents' 'topic-ai-slop' 'topic-claude-code' 'topic-code-quality' 'topic-code-review' 'topic-codex' 'topic-cursor' 'topic-laravel' 'topic-nodejs' 'topic-react' 'trait':253 'transcript':593 'trash':530 'trigger':29 'type':470 'unclear':204 'unit':19,332 'uploadedfil':572 'use':14,139,162,170,197,218,255,258,284,458,672,676,681,728,732,737,744,788 'user':207,675,686,692,693,698,718,720,731,750,756,757,762,782,784 'vari':491 'version':68 'void':236,269,277,755 'vs':454,543 'without':155 'work':315 'world':703,711,717,767,775,781 'write':16,107,127,329 'yaml':803","prices":[{"id":"1823737a-e743-48c0-b162-98804eb3e70e","listingId":"9b18a703-66fc-4e6e-b568-dd8f605c5fff","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"AsyrafHussin","category":"agent-skills","install_from":"skills.sh"},"createdAt":"2026-05-16T18:57:14.590Z"}],"sources":[{"listingId":"9b18a703-66fc-4e6e-b568-dd8f605c5fff","source":"github","sourceId":"AsyrafHussin/agent-skills/laravel-testing","sourceUrl":"https://github.com/AsyrafHussin/agent-skills/tree/main/skills/laravel-testing","isPrimary":false,"firstSeenAt":"2026-05-16T18:57:14.590Z","lastSeenAt":"2026-05-18T18:58:24.959Z"}],"details":{"listingId":"9b18a703-66fc-4e6e-b568-dd8f605c5fff","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"AsyrafHussin","slug":"laravel-testing","github":{"repo":"AsyrafHussin/agent-skills","stars":39,"topics":["agent-rules","agent-skills","ai-agents","ai-slop","claude-code","code-quality","code-review","codex","cursor","laravel","nodejs","react","technical-debt","typescript","windsurf"],"license":"mit","html_url":"https://github.com/AsyrafHussin/agent-skills","pushed_at":"2026-05-16T19:24:02Z","description":"Agent skills for AI coding agents (Claude Code, Cursor, Codex, Windsurf) — Laravel, React, TypeScript, MySQL, code quality, technical debt, documentation, and security.","skill_md_sha":"52cec1fa31004d4f22465a920d1a01abc1885d4c","skill_md_path":"skills/laravel-testing/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/AsyrafHussin/agent-skills/tree/main/skills/laravel-testing"},"layout":"multi","source":"github","category":"agent-skills","frontmatter":{"name":"laravel-testing","license":"MIT","description":"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."},"skills_sh_url":"https://skills.sh/AsyrafHussin/agent-skills/laravel-testing"},"updatedAt":"2026-05-18T18:58:24.959Z"}}