laravel-tester

Generates modern maintainable Laravel applications unit and feature (integration) tests with a focus on performance by default and for best testing style practices.

$ 安裝

git clone https://github.com/Mentor-Wizard/mentor-wizard-webapp /tmp/mentor-wizard-webapp && cp -r /tmp/mentor-wizard-webapp/.claude/skills/laravel-tester ~/.claude/skills/mentor-wizard-webapp

// tip: Run this command in your terminal to install the skill


name: laravel-tester description: Generates modern maintainable Laravel applications unit and feature (integration) tests with a focus on performance by default and for best testing style practices.

Laravel Tester

Instructions

Models Testing Policy

  • DO NOT create unit tests for Laravel Eloquent models.
  • Rationale:
    • Laravel's Eloquent ORM is extensively tested by the Laravel team
    • Testing basic CRUD operations, relationships, and standard functionality provides no value
    • Models are excluded from code coverage metrics (see phpunit.xml)
  • What NOT to test:
    • Basic relationships (hasOne, hasMany, belongsTo, etc.)
    • Simple CRUD operations (create, update, delete, find)
    • Standard Eloquent functionality
    • Factory creation without custom logic
    • Basic fillable/guarded attributes
    • Standard casting functionality
  • Exceptions — What TO test:
    • Custom business logic methods
    • Complex accessors/mutators with business rules
    • Custom scopes with specific logic
    • Observer behavior and side effects
    • Mass assignment protection (if critical)
  • Where to test model functionality instead:
    • Feature tests via HTTP endpoints and workflows
    • Integration tests for model interactions
    • Observer tests for event handlers
    • Action/Service tests for business logic

Framework

  • Pest PHP - Modern testing framework with BDD-style syntax
  • Mutation Testing with Infection for test quality assurance
  • Architectural Testing to enforce code structure rules

Test Structure

tests/
├── Feature/          # Integration tests
│   ├── Auth/
│   ├── MentorPrograms/
│   └── Pages/
├── Unit/             # Unit tests
│   ├── Actions/
│   ├── Models/
│   ├── Observers/
│   └── Support/
├── Pest.php         # Pest configuration
└── TestCase.php     # Base test case

Running Tests

All tests must be run in a Docker container. Feature tests do not need to mutate.

With Docker

# Run all tests
docker compose exec app ./vendor/bin/pest

# Run with coverage
docker compose exec app ./vendor/bin/pest --coverage

# Run mutation testing
docker compose exec app ./vendor/bin/pest --mutate --covered-only --parallel --min=100

# Run specific test file
docker compose exec app ./vendor/bin/pest tests/Unit/ExampleTest.php

Local Environment

# Ensure test database is configured in .env.testing
./vendor/bin/pest
./vendor/bin/pest --coverage
./vendor/bin/pest --mutate --covered-only --parallel --min=100

Test Configuration

  • Database: Uses RefreshDatabase trait for clean test state
  • Environment: Configured in phpunit.xml with testing-specific settings
  • Coverage: Reports generated in coverage/ directory
  • Memory Limit: 512M for test execution

Writing Tests

Basic Test Structure

<?php

declare(strict_types=1);

// For mutation testing (optional)
mutates(YourClass::class);

describe('Feature Description', function (): void {
    beforeEach(function (): void {
        // Setup code
        $this->user = User::factory()->create();
    });

    it('describes what it tests', function (): void {
        $result = someFunction();

        expect($result)->toBe('expected_value');
    });
});

Testing Actions (Laravel Actions Pattern)

it('handles the action correctly', function (): void {
    $action = new YourAction();
    $result = $action->handle($request, $parameters);

    expect($result)
        ->toBeInstanceOf(RedirectResponse::class)
        ->and($result->getTargetUrl())
        ->toBe(route('expected.route'));
});

Architectural Testing

The project enforces architectural rules via tests/Unit/ArchTest.php:

  • No debugging functions in production code
  • Models must extend Eloquent Model
  • Page actions must have 'Page' suffix
  • Enums must be proper enum classes

Common Commands

# Code quality checks
./vendor/bin/phpstan analyse
./vendor/bin/pint --test
./vendor/bin/rector process --dry-run

# Fix code issues
./vendor/bin/pint
./vendor/bin/rector process

# Generate IDE helpers
php artisan ide-helper:generate
php artisan ide-helper:models

# Clear caches
php artisan config:clear
php artisan cache:clear
php artisan view:clear

Code Quality Tools

  1. Laravel Pint - Code formatting based on Laravel preset with strict rules
  2. PHPStan (Level 5) - Static analysis with Larastan for Laravel-specific checks
  3. Rector - Automated code modernization for PHP 8.4 and Laravel 12.0
  4. Cognitive Complexity - Limits complexity (class: 85, function: 8)

Development Tools

  • Telescope: Laravel debugging assistant (enabled in testing)
  • Log Viewer: Web-based log viewing interface
  • IDE Helpers: Comprehensive IDE support with auto-generated helpers
  • Xdebug: Available in Docker development environment

Pest

Testing

  • If you need to verify a feature is working, write or update a Unit / Feature test.

Pest Tests

  • All tests must be written using Pest. Use php artisan make:test --pest <name>.
  • You must not remove any tests or test files from the tests directory without approval. These are not temporary or helper files - these are core to the application.
  • Tests should test all of the happy paths, failure paths, and weird paths.
  • Tests live in the tests/Feature and tests/Unit directories.
  • Pest tests look and behave like this: it('is true', function () { expect(true)->toBeTrue(); });

Running Tests

  • Run the minimal number of tests using an appropriate filter before finalizing code edits.
  • To run all tests: php artisan test.
  • To run all tests in a file: php artisan test tests/Feature/ExampleTest.php.
  • To filter on a particular test name: php artisan test --filter=testName (recommended after making a change to a related file).
  • When the tests relating to your changes are passing, ask the user if they would like to run the entire test suite to ensure everything is still passing.

Pest Assertions

  • When asserting status codes on a response, use the specific method like assertForbidden and assertNotFound instead of using assertStatus(403) or similar, e.g.: it('returns all', function () { $response = $this->postJson('/api/docs', []);

    $response->assertSuccessful(); });

Mocking

  • Mocking can be very helpful when appropriate.
  • When mocking, you can use the Pest\Laravel\mock Pest function, but always import it via use function Pest\Laravel\mock; before using it. Alternatively, you can use $this->mock() if existing tests do.
  • You can also create partial mocks using the same import or self method.

Datasets

  • Use datasets in Pest to simplify tests which have a lot of duplicated data. This is often the case when testing validation rules, so consider going with this solution when writing tests for validation rules.

Pest 4

  • Pest v4 is a huge upgrade to Pest and offers: browser testing, smoke testing, visual regression testing, test sharding, and faster type coverage.
  • Browser testing is incredibly powerful and useful for this project.
  • Browser tests should live in tests/Browser/.
  • Use the search-docs tool for detailed guidance on utilizing these features.

Browser Testing

  • You can use Laravel features like Event::fake(), assertAuthenticated(), and model factories within Pest v4 browser tests, as well as RefreshDatabase (when needed) to ensure a clean state for each test.
  • Interact with the page (click, type, scroll, select, submit, drag-and-drop, touch gestures, etc.) when appropriate to complete the test.
  • If requested, test on multiple browsers (Chrome, Firefox, Safari).
  • If requested, test on different devices and viewports (like iPhone 14 Pro, tablets, or custom breakpoints).
  • Switch color schemes (light/dark mode) when appropriate.
  • Take screenshots or pause tests for debugging when appropriate.

Example Tests

$this->actingAs(User::factory()->create());

$page = visit('/sign-in'); // Visit on a real browser...

$page->assertSee('Sign In')
    ->assertNoJavascriptErrors() // or ->assertNoConsoleLogs()
    ->click('Forgot Password?')
    ->fill('email', 'nuno@laravel.com')
    ->click('Send Reset Link')
    ->assertSee('We have emailed your password reset link!')

Notification::assertSent(ResetPassword::class);

});

$pages->assertNoJavascriptErrors()->assertNoConsoleLogs();

Test Enforcement

  • Every change must be programmatically tested. Write a new test or update an existing test, then run the affected tests to make sure they pass.
  • Run the minimum number of tests needed to ensure code quality and speed. Use php artisan test with a specific filename or filter.

Laravel Pint Code Formatter

  • You must run vendor/bin/pint --dirty before finalizing changes to ensure your code matches the project's expected style.
  • Do not run vendor/bin/pint --test, simply run vendor/bin/pint to fix any formatting issues.