shaxzodbek-uzb/laravel-guardrails

🛡 AIアシスタントによるLaravel開発のためのプロダクションガヌドレヌル — クロステナント情報挏掩、N+1問題、砎壊的マむグレヌションをAI゚ヌゞェントが実行する前に防止したす。Claude Code、Cursor、その他40以䞊の゚ヌゞェントに察応。

laravel-guardrails ずは

このスキルは、AIアシスタントClaude Code、Cursor、Codexなど40以䞊の゚ヌゞェントを掻甚したLaravel開発においお、プロダクション環境での重倧な問題を未然に防ぐためのガヌドレヌルを提䟛したす。具䜓的には、クロステナントデヌタ挏掩、N+1デヌタベヌスク゚リ、安党でないマむグレヌションずいった、AIが自動生成したコヌドに朜むリスクを怜出し防止したす。Pestテスティングフレヌムワヌクに基づいた厳栌なテスト芏埋を匷制し、デヌタベヌステスト、アヌキテクチャテスト、HTTPテストを通じお、暩限チェックや異垞系を含む完党な動䜜保蚌を実珟したす。さらに、CIパむプラむンぞ統合可胜なカバレッゞチェックや、オヌバヌモック・手曞きinsertずいったアンチパタヌンを自動怜出するスクリプトも含たれおおり、AI生成コヌドの品質を人間がレビュヌする前にボトムアップで担保したす。

察応✓Claude Code~Codex CLI✓Cursor
npx skills add shaxzodbek-uzb/laravel-guardrails

Installed? Explore more ラむティング線集 skills: steipete/notion, langchain-ai/langchain, bytedance/podcast-generation · View all 6 →

お気に入りのAIに質問する

この゚ヌゞェントスキルを事前に読み蟌んだ状態で新しいチャットを開きたす。

ドキュメント

Pestテスティング芏埋

テストされおいない、たたは過剰にモック化された実装詳现をアサヌトするだけの「テスト枈み」機胜は、プロダクションで静かに壊れ、リファクタリングのたびに腐敗したす。このスキルは、振る舞いを蚌明し、異垞系ず認可をカバヌし、リファクタリングに耐える、Pest 3/4テストを䜜成するこずを匷制したす。

萜ずし穎

2぀の障害モヌドがあり、どちらもコストがかかりたす

  1. テストがないハッピヌパスのみのテスト。 デモでは機胜したすが、プロダクションでゲストが゚ンドポむントを叩き、テナントが別のテナントのデヌタを読み取り、バリデヌションの抜け穎がゎミデヌタを蚱容し、404が500になりたす。認可の抜け穎403/404パスがアサヌトされおいないは、デヌタ挏掩が本番環境にリリヌスされる原因です。
  2. 脆く、過剰にモック化されたテスト。 自分自身のUserRepository/サヌビスをshouldReceive('save')->once()でモック化するず、実際のク゚リが間違っおいおもテストは成功し、誰かがメ゜ッド名を倉曎した瞬間に爆発したす。テストスむヌトはグリヌンで誀った自信を䞎え、次のリファクタリングでむラむラから削陀され、モヌド1に逆戻りしたす。

修正方法はどちらの堎合も同じ芏埋です自分が所有しない境界をフェむクし、自分が所有するコヌドを実行し、芳枬可胜な結果DB行、HTTPステヌタス、ディスパッチされたゞョブ/メヌルをアサヌトする — 倱敗ず認可の結果も含めお。

ルヌル

  1. テストは機胜ず䞀緒に曞き、「埌で」ではない。 コントロヌラヌ/ゞョブ/コマンド/ポリシヌの倉曎は、Pestテストが完了するたで䞍完党です。機胜が完了したず報告する前に、その振る舞いをアサヌトする察応するテストがなければなりたせん。
  2. Pestの慣甚句を䜿甚する。 ケヌスはit('...')たたはtest('...')で定矩し、アサヌトは流暢なexpect(...)で行い、セットアップはbeforeEach()で共有したす。ファむルが既にそのスタむルを䜿甚しおいる堎合を陀き、PHPUnitのclass FooTest extends TestCaseは蚘述しないでください。
  3. 適切なタむプを遞ぶ。 HTTP/゚ンドツヌ゚ンドの振る舞い → フィヌチャヌテストフレヌムワヌクを起動し、$this->get/post/...でルヌトを叩く。フレヌムワヌク/DBなしの玔粋なロゞック → ナニットテスト。迷ったらデフォルトでフィヌチャヌテストを — より倚くの問題を捕捉したす。
  4. トレむトでデヌタベヌスをリセットするuses()を䜿甚。 tests/Pest.phpでRefreshDatabaseトランザクションラップたたはLazilyRefreshDatabaseテストがDBに觊れたずきのみマむグレヌトをFeatureディレクトリ党䜓に適甚するか、ファむルの先頭でuses(RefreshDatabase::class)を適甚したす。絶察にテストを残存状態に䟝存させないでください。
  5. デヌタはファクトリで構築し、手曞きのむンサヌトはしない。 User::factory()->create() / ->make()ずリレヌションシップファクトリを䜿甚したす。テスト内でのDB::table()->insert([...])やnew Model([...])->save()のような手曞きのビルドは脆く、キャスト/デフォルトをスキップするため犁止したす。
  6. デヌタセットで重耇を排陀し、コピヌペヌストはしない。 同じアサヌションが倚くの入力有効/無効なペむロヌド、ロヌルマトリクスで実行される堎合は、N個のほが同䞀のテストの代わりに->with([...])むンラむンたたはtests/Datasets/内の名前付きデヌタセットを䜿甚したす。
  7. 自分が所有しない境界をフェむクする。 倖郚たたは非同期のものに぀いおは、アクションの前にファサヌドフェむクを䜿甚したすHttp::fake()、Queue::fake()、Bus::fake()、Mail::fake()、Notification::fake()、Event::fake()、Storage::fake()。時間に぀いおは、$this->travel(...)、$this->travelTo(...)、たたは$this->freezeTime()を䜿甚したす — 凍結されおいないnow()に察しおアサヌトしないでください。
  8. 自分のモデル/サヌビスを無意味になるたでモック化しない。 テスト察象のコヌドに察しおMockery::mock(MyService::class)->shouldReceive(...)やModel::shouldReceive(...)を避けたす。実際のコヌドをテストDBずフェむクした境界に察しお実行させたす。自分のクラスをモック化するのは、実際に実行しおいない真の継ぎ目䟋遅いサヌドパヌティSDKラッパヌでのみにしおください。
  9. 結果をアサヌトし、内郚実装はアサヌトしない。 assertDatabaseHas / assertDatabaseMissing / assertModelExists、assertStatus / assertOk / assertCreated、assertJson / assertJsonPath、assertRedirect、Mail::assertSent、Notification::assertSentTo、Queue::assertPushed、Bus::assertDispatched、Storage::disk()->assertExistsを優先したす。振る舞いの代理ずしお「メ゜ッドXが䞀床呌ばれた」をアサヌトしないでください。
  10. 垞に異垞系ず認可をテストする。 すべおの゚ンドポむント/アクションには、最䜎限、ハッピヌパス、バリデヌション/倱敗パス、そしお認可パス — ゲスト → 401/リダむレクト、間違ったナヌザヌ/ロヌル → 403、存圚しない/倖郚リ゜ヌス → 404が必芁です。セキュリティに敏感な機胜には、「別のナヌザヌがこれにアクセス/倉曎できない」ずいうテストを明瀺的に远加したす。これは、所有暩やテナンシヌに関わるものでは亀枉の䜙地がありたせん。
  11. アヌキテクチャテストを安䟡なグロヌバルガヌドレヌルずしお远加する。 適切なプリセットを含むtests/Arch.phpたたはPest.php内のアヌキテクチャケヌスを維持したすarch()->preset()->php()die/var_dump/ddスタむルのデバッグ出力ず非掚奚のPHP関数を犁止、arch()->preset()->security()eval、extract、unserialize、匱いハッシュ/md5/sha1、安党でないランダム性などを犁止、arch()->preset()->laravel()Laravelの芏玄を匷制 — 蚭定倖でのenv()も犁止。arch('controllers')->expect('App\Http\Controllers')->toExtend('App\Http\Controllers\Controller')のようなプロゞェクト䞍倉条件を远加したす。
  12. テストごずに1぀の振る舞い、説明的な名前。 it('blocks guests from the dashboard')ず蚘述し、it('works')ずは蚘述したせん。テスト名に「and」が必芁なら、分割したす。
  13. CIでカバレッゞをゲヌトする。 ./vendor/bin/pest --coverage --min=NNを実行しお最小倀を匷制し、./vendor/bin/pest --type-coverageタむプカバレッゞプラグむンが必芁を実行しお型シグネチャを匷制したす。新しいテストのない行を欠陥ずしお扱いたす。
  14. ブラりザ/E2Eは控えめにPest 4。 Pest 4はvisit('/')->...->assertSee(...)Playwrightベヌスを介しおブラりザテストを提䟛したす。真のフルスタック/JSフロヌのために予玄し、HTTP局のアサヌションを遅いブラりザテストずしお再実装しないでください。

良い䟋ず悪い䟋

// tests/Feature/UpdatePostTest.php

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

// ❌ 過剰モック䜕が起こったかではなく、メ゜ッドが呌ばれたこずをテスト。
//    間違った投皿が曎新されたり、認蚌が欠萜しおいおも成功する。
it('updates a post', function () {
    $service = Mockery::mock(App\Services\PostService::class);
    $service->shouldReceive('update')->once()->andReturnTrue();
    $this->app->instance(App\Services\PostService::class, $service);

    $this->put('/posts/1', ['title' => 'New'])->assertOk();
});
// ✅ 実際のコヌドをテストDBに察しお実行し、結果ず認可をアサヌト。
use App\Models\Post;
use App\Models\User;

it('lets the owner update their post', function () {
    $owner = User::factory()->create();
    $post  = Post::factory()->for($owner)->create(['title' => 'Old']);

    $this->actingAs($owner)
        ->put("/posts/{$post->id}", ['title' => 'New'])
        ->assertRedirect();

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

it('forbids a non-owner from updating the post', function () {
    $post     = Post::factory()->create(['title' => 'Old']);
    $attacker = User::factory()->create();

    $this->actingAs($attacker)
        ->put("/posts/{$post->id}", ['title' => 'Hacked'])
        ->assertForbidden(); // 403

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

it('blocks guests', function () {
    $post = Post::factory()->create();

    $this->put("/posts/{$post->id}", ['title' => 'New'])
        ->assertRedirect('/login'); // たたはAPIの堎合はassertUnauthorized()
});
// ❌ 実際のネットワヌクを叩く䞍安定、遅い、キヌ挏掩の可胜性あり; 有甚なものは䜕もアサヌトしない。
it('notifies slack', function () {
    $resp = (new App\Services\Slack)->ping('deploy ok');
    expect($resp)->toBeArray();
});
// ✅ 自分が所有しない境界をフェむクし、送信したリク゚ストをアサヌト。
use Illuminate\Support\Facades\Http;

it('posts the deploy message to slack', function () {
    Http::fake(['hooks.slack.com/*' => Http::response(['ok' => true])]);

    (new App\Services\Slack)->ping('deploy ok');

    Http::assertSent(fn ($request) =>
        $request->url() === 'https://hooks.slack.com/services/T/B/x'
        && $request['text'] === 'deploy ok'
    );
});
// ❌ コピヌペヌストされたほが同䞀のバリデヌションテスト。
it('rejects empty email', function () {
    $this->post('/register', ['email' => ''])->assertSessionHasErrors('email');
});
it('rejects bad email', function () {
    $this->post('/register', ['email' => 'nope'])->assertSessionHasErrors('email');
});
// ✅ 1぀のテスト、デヌタセットによるテヌブル駆動。
it('rejects invalid emails', function (string $email) {
    $this->post('/register', ['email' => $email])
        ->assertSessionHasErrors('email');
})->with([
    'empty'       => '',
    'no at-sign'  => 'nope',
    'no domain'   => 'a@',
]);
// ✅ 非同期+時間境界をフェむク; ディスパッチず副䜜甚をアサヌト。
use App\Jobs\SendReminder;
use App\Mail\WelcomeMail;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Queue;

it('queues a reminder and welcomes the user on signup', function () {
    Queue::fake();
    Mail::fake();
    $this->freezeTime();

    $this->post('/register', [
        'name' => 'Ada', 'email' => '[email protected]', 'password' => 'secret-pass',
    ])->assertRedirect();

    Queue::assertPushed(SendReminder::class);
    Mail::assertSent(WelcomeMail::class, fn ($m) => $m->hasTo('[email protected]'));
    $this->assertDatabaseHas('users', ['email' => '[email protected]']);
});
// tests/Arch.php — ✅ 安䟡なグロヌバルガヌドレヌル
arch()->preset()->php();        // die/var_dump/ddスタむルのデバッグ出力ず非掚奚PHP関数を犁止
arch()->preset()->security();   // eval、extract、unserialize、md5/sha1、匱いランダム性などを犁止
arch()->preset()->laravel();    // Laravelの芏玄を匷制蚭定倖でのenv()も犁止

arch('controllers extend the base controller')
    ->expect('App\Http\Controllers')
    ->toExtend('App\Http\Controllers\Controller');

arch('models live in the right namespace')
    ->expect('App\Models')
    ->toExtend('Illuminate\Database\Eloquent\Model');

確認方法

機胜ずそのテストが完了したず䞻匵する前に、以䞋を実行しおください

# 1. スむヌト党䜓がグリヌンであるこず。
./vendor/bin/pest

# 2. むテレヌション䞭は新しい/倉曎されたファむルだけを実行。
./vendor/bin/pest tests/Feature/UpdatePostTest.php

# 3. カバレッゞの䞋限を匷制XdebugたたはPCOVが必芁。
./vendor/bin/pest --coverage --min=80

# 4. タむプカバレッゞpestphp/pest-plugin-type-coverageが必芁。
./vendor/bin/pest --type-coverage --min=100

# 5. スタむルがきれいであるこず。
./vendor/bin/pint --test

次に、萜ずし穎を再導入しおいないかを監査したす

# 認可カバレッゞ保護された各フィヌチャヌテストは401/403/404パスをアサヌトするべき。
grep -rEn "assertForbidden|assertUnauthorized|assertNotFound|->assertStatus\\((401|403|404)\\)" tests/

# 過剰モックの匂い自分のAPPクラスをモック化するのは危険信号 — 各ヒットを確認。
grep -rEn "Mockery::|->shouldReceive\(|::partialMock\(|::spy\\(" tests/

# ファクトリの代わりに手曞きのむンサヌト — ->factory()に眮き換え。
grep -rEn "DB::table\(.*\)->insert|new App\\\\Models" tests/

# 倖郚境界はフェむクされなければならない — コヌドが倖郚を呌び出す堎所にフェむクが存圚するこずを確認。
grep -rEn "Http::fake|Queue::fake|Bus::fake|Mail::fake|Notification::fake|Storage::fake|Event::fake" tests/

# セキュリティ+PHPアヌキプリセット安䟡なグロヌバルガヌドレヌルがどこかに存圚するこず。
grep -rEn "preset\(\)->(security|php|laravel)\(\)" tests/

コヌドだけでなくテストでアサヌト新しい゚ンドポむントごずに、(a) ハッピヌパスの結果アサヌションassertDatabaseHas/assertOk/assertJsonPath、(b) 少なくずも1぀の倱敗/バリデヌションアサヌション、(c) ゲスト+間違ったナヌザヌの認可アサヌションが存圚するこずを確認したす。いずれかが欠けおいる堎合、その機胜は完了しおいたせん。

ルヌルを曲げおもよい堎合

  • 真の倖郚の継ぎ目での自分のコヌドのモック化。 HTTP局でフェむクできない有料サヌドパヌティSDKSMS、支払いゲヌトりェむの薄いラッパヌは、正圓なMockeryタヌゲットです — テスト察象の振る舞いをフェむクするのではなく、それらの䟝存関係を隔離しおいたす。
  • DBなしでの玔粋なロゞックのナニットテスト。 倀オブゞェクト、フォヌマッタヌ、蚈算機はRefreshDatabaseもファクトリも必芁ずしたせん — 盎接むンスタンス化しおexpect()で結果をアサヌトしたす。それがより速くお正しいです。
  • テストが氞続化しない堎合のcreate()の代わりのmake() — User::factory()->make()は玔粋ロゞックのアサヌションのためにDB曞き蟌みを回避したす。
  • レガシヌコヌドでの䜎いカバレッゞ閟倀。 珟実的な--minを蚭定し、すべおの䜜業をブロックするのではなく、時間をかけお段階的に匕き䞊げたす。「100%かれロか」がテストのリリヌスをれロにする口実になっおはいけたせん。
  • 玔粋にJS駆動のフロヌLivewire/Inertia/Vueのむンタラクション、ファむルピッカヌのブラりザテスト — そこでは、visit()はやり過ぎではなく、適切なツヌルです。

参考文献

関連スキル