Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message
pesttesting
codebar-ag/coding-guidelines
test
refactor
review
pest
testing
datasets
http-assertions
refresh-database
fakes
aaa-pattern

Laravel testing conventions using the Pest PHP framework. Covers test structure, AAA pattern, HTTP assertions, datasets, mocking, browser tests, and architecture tests.

Install
php artisan boost:add-skill codebar-ag/coding-guidelines

Save this content to: .claude/skills/pesttesting/SKILL.md

# Pest Testing

## When to Use

- Writing new tests in projects that standardize on Pest syntax.
- Refactoring older tests into fluent, readable behavior descriptions.
- Adding datasets, architecture tests, and expressive HTTP assertions.

## When Not to Use

- Existing areas intentionally locked to class-based PHPUnit style.
- Browser tests without real browser requirements (use feature tests instead).
- Non-test automation scripts where Pest semantics do not apply.

## Preconditions

- Pest is installed and configured for Laravel (`vendor/bin/pest` works).
- Test bootstrap and DB setup are available (`phpunit.xml`/env test config).
- Team conventions for test file placement and naming are understood.
- Fakes/mocks strategy is clear for external integrations.

## Process Checklist

- [ ] Write tests in Pest syntax (`it`, `describe`, `expect`).
- [ ] Structure each test as AAA with concise comments.
- [ ] Use named response assertions (`assertCreated`, `assertForbidden`, etc.).
- [ ] Add `uses(RefreshDatabase::class)` for DB-touching files.
- [ ] Replace duplicated cases with datasets.
- [ ] Run targeted tests first, then full impacted scope.

## Rules

- Use Pest syntax consistently for new and refactored tests.
- Use named HTTP assertions, not raw numeric status checks.
- Prefer Laravel fakes for framework integrations (events, mail, queue, notifications).
- Keep browser-test constraints aligned with the `pest-browser` skill.
- Never delete tests without explicit approval.

## Examples

```php
// Basic test — AAA pattern
uses(RefreshDatabase::class);

it('creates an invoice for the given order', function () {
    // Arrange
    $order = Order::factory()->create();

    // Act
    $response = $this->postJson('/api/invoices', ['order_id' => $order->id]);

    // Assert
    $response->assertCreated();
    expect(Invoice::count())->toBe(1);
});
```

```php
// Grouped with describe() and beforeEach()
describe('InvoiceController', function () {
    beforeEach(function () {
        $this->user = User::factory()->create();
        $this->actingAs($this->user);
    });

    it('lists all invoices', function () {
        Invoice::factory()->count(3)->create();
        $this->getJson('/api/invoices')->assertSuccessful();
    });

it('creates an invoice', function () {
        $order = Order::factory()->create();
        $this->postJson('/api/invoices', ['order_id' => $order->id])
            ->assertCreated();
    });
});
```

```php
// Datasets for multiple inputs
it('rejects invalid email addresses', function (string $email) {
    $this->postJson('/api/users', ['email' => $email])
        ->assertUnprocessable();
})->with([
    'empty string'  => [''],
    'missing @'     => ['notanemail'],
    'missing tld'   => ['user@domain'],
]);
```

```php
// Named HTTP assertion methods
$response->assertSuccessful();    // ❌ assertStatus(200)
$response->assertCreated();       // ❌ assertStatus(201)
$response->assertNoContent();     // ❌ assertStatus(204)
$response->assertUnprocessable(); // ❌ assertStatus(422)
$response->assertForbidden();     // ❌ assertStatus(403)
$response->assertUnauthorized();  // ❌ assertStatus(401)
$response->assertNotFound();      // ❌ assertStatus(404)
```

```php
// Architecture tests
arch('no debug calls in production code')
    ->expect('App')
    ->not->toUse(['dd', 'dump', 'var_dump', 'ray']);

arch('controllers have the correct suffix')
    ->expect('App\Http\Controllers')
    ->toHaveSuffix('Controller');
```

```php
// Browser test baseline with Pest browser (Playwright), in tests/Browser
it('submits invoice flow in browser', function () {
    $this->actingAs(User::factory()->create());

    visit('/invoices/create')
        ->assertNoJavaScriptErrors()
        ->press('Create invoice')
        ->assertSee('Invoice created');
});
```

## Run Commands

```bash
# Run all tests through Laravel
php artisan test

# Run Pest directly
vendor/bin/pest

# Run a specific file
vendor/bin/pest tests/Feature/InvoiceControllerTest.php

# Filter by test name/description
vendor/bin/pest --filter="creates an invoice"

# Run a focused group (if groups are configured)
vendor/bin/pest --group=feature
```

## Testing Guidance

- Keep tests behavior-focused and easy to read at a glance.
- Use datasets and `beforeEach()` to reduce duplication before adding new assertions.
- For browser behavior, defer selector/waiting details to `pest-browser/SKILL.md`.

## Anti-Patterns

- Using `assertStatus(200)` instead of named assertion methods
- Using Laravel Dusk for browser tests — use Pest browser (`pest-browser` skill)
- Using fixed waits (`wait(3)`) in browser tests — use waiting assertions such as `assertSee()`
- Repeating the same test for multiple inputs instead of using datasets
- Using CSS class selectors in browser tests — use text, labels or `data-testid` (`@name`)
- Missing `uses(RefreshDatabase::class)` in feature/unit files that touch the database
- Deleting a test without explicit approval
- Missing `assertNoJavaScriptErrors()` after `visit()` in browser tests

## References

- [Pest PHP Documentation](https://pestphp.com/)
- [Laravel Testing](https://laravel.com/docs/testing)
- Related: `pest-browser/SKILL.md` — full browser (E2E) test conventions
- Related: `PHPStan/SKILL.md` — static analysis runs alongside tests

Pest Testing

When to Use

  • Writing new tests in projects that standardize on Pest syntax.
  • Refactoring older tests into fluent, readable behavior descriptions.
  • Adding datasets, architecture tests, and expressive HTTP assertions.

When Not to Use

  • Existing areas intentionally locked to class-based PHPUnit style.
  • Browser tests without real browser requirements (use feature tests instead).
  • Non-test automation scripts where Pest semantics do not apply.

Preconditions

  • Pest is installed and configured for Laravel (vendor/bin/pest works).
  • Test bootstrap and DB setup are available (phpunit.xml/env test config).
  • Team conventions for test file placement and naming are understood.
  • Fakes/mocks strategy is clear for external integrations.

Process Checklist

  • Write tests in Pest syntax (it, describe, expect).
  • Structure each test as AAA with concise comments.
  • Use named response assertions (assertCreated, assertForbidden, etc.).
  • Add uses(RefreshDatabase::class) for DB-touching files.
  • Replace duplicated cases with datasets.
  • Run targeted tests first, then full impacted scope.

Rules

  • Use Pest syntax consistently for new and refactored tests.
  • Use named HTTP assertions, not raw numeric status checks.
  • Prefer Laravel fakes for framework integrations (events, mail, queue, notifications).
  • Keep browser-test constraints aligned with the pest-browser skill.
  • Never delete tests without explicit approval.

Examples

// Basic test — AAA pattern
uses(RefreshDatabase::class);

it('creates an invoice for the given order', function () {
    // Arrange
    $order = Order::factory()->create();

    // Act
    $response = $this->postJson('/api/invoices', ['order_id' => $order->id]);

    // Assert
    $response->assertCreated();
    expect(Invoice::count())->toBe(1);
});
// Grouped with describe() and beforeEach()
describe('InvoiceController', function () {
    beforeEach(function () {
        $this->user = User::factory()->create();
        $this->actingAs($this->user);
    });

    it('lists all invoices', function () {
        Invoice::factory()->count(3)->create();
        $this->getJson('/api/invoices')->assertSuccessful();
    });

it('creates an invoice', function () {
        $order = Order::factory()->create();
        $this->postJson('/api/invoices', ['order_id' => $order->id])
            ->assertCreated();
    });
});
// Datasets for multiple inputs
it('rejects invalid email addresses', function (string $email) {
    $this->postJson('/api/users', ['email' => $email])
        ->assertUnprocessable();
})->with([
    'empty string'  => [''],
    'missing @'     => ['notanemail'],
    'missing tld'   => ['user@domain'],
]);
// Named HTTP assertion methods
$response->assertSuccessful();    // ❌ assertStatus(200)
$response->assertCreated();       // ❌ assertStatus(201)
$response->assertNoContent();     // ❌ assertStatus(204)
$response->assertUnprocessable(); // ❌ assertStatus(422)
$response->assertForbidden();     // ❌ assertStatus(403)
$response->assertUnauthorized();  // ❌ assertStatus(401)
$response->assertNotFound();      // ❌ assertStatus(404)
// Architecture tests
arch('no debug calls in production code')
    ->expect('App')
    ->not->toUse(['dd', 'dump', 'var_dump', 'ray']);

arch('controllers have the correct suffix')
    ->expect('App\Http\Controllers')
    ->toHaveSuffix('Controller');
// Browser test baseline with Pest browser (Playwright), in tests/Browser
it('submits invoice flow in browser', function () {
    $this->actingAs(User::factory()->create());

    visit('/invoices/create')
        ->assertNoJavaScriptErrors()
        ->press('Create invoice')
        ->assertSee('Invoice created');
});

Run Commands

# Run all tests through Laravel
php artisan test

# Run Pest directly
vendor/bin/pest

# Run a specific file
vendor/bin/pest tests/Feature/InvoiceControllerTest.php

# Filter by test name/description
vendor/bin/pest --filter="creates an invoice"

# Run a focused group (if groups are configured)
vendor/bin/pest --group=feature

Testing Guidance

  • Keep tests behavior-focused and easy to read at a glance.
  • Use datasets and beforeEach() to reduce duplication before adding new assertions.
  • For browser behavior, defer selector/waiting details to pest-browser/SKILL.md.

Anti-Patterns

  • Using assertStatus(200) instead of named assertion methods
  • Using Laravel Dusk for browser tests — use Pest browser (pest-browser skill)
  • Using fixed waits (wait(3)) in browser tests — use waiting assertions such as assertSee()
  • Repeating the same test for multiple inputs instead of using datasets
  • Using CSS class selectors in browser tests — use text, labels or data-testid (@name)
  • Missing uses(RefreshDatabase::class) in feature/unit files that touch the database
  • Deleting a test without explicit approval
  • Missing assertNoJavaScriptErrors() after visit() in browser tests

References

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource