Pest PHP Testing Framework

website·Indexed 35 days ago

https://pestphp.com/

A PHP testing framework featuring an Expectation API, architecture testing, and advanced tools for mutation testing, type coverage, and browser testing. Includes support for higher-order testing, custom helpers, and integration with PHPStan and Rector.

Tokens
40.8K
Snippets
301
Records
326
Agent score
72%

What's inside Pest

  1. Overview of Pest PHP Testing Framework

    Pest is an elegant testing framework for PHP designed to make tests simple to read and understand using a syntax that resembles natural human language. Its API is inspired by Ruby's RSpec and Jest, providing a seamless coding experience and highly organized, informative console reporting for debugging with clear error and stack trace displays.
    function sum($a, $b) {
        return $a + $b;
    }
    
    test('sum', function () {
      $result = sum(1, 2);
    
      expect($result)->toBe(3);
    });
  2. Introduction to Pest PHP Architecture Testing

    Architecture testing in Pest PHP allows you to define expectations to ensure your application adheres to specific architectural rules. You can target files using relative namespaces, fully qualified namespaces, or specific function names. This helps maintain a clean codebase by catching design flaws early.
    arch()
        ->expect('App')
        ->toUseStrictTypes()
        ->not->toUse(['die', 'dd', 'dump']);
    
    arch()
        ->expect('App\Models')
        ->toBeClasses()
        ->toExtend('Illuminate\Database\Eloquent\Model')
        ->toOnlyBeUsedIn('App\Repositories')
        ->ignoring('App\Models\User');
    
    arch()
        ->expect('App\Http')
        ->toOnlyBeUsedIn('App\Http');
        
    arch()
        ->expect('App\*\Traits')
        ->toBeTraits();
    
    arch()->preset()->php();
    arch()->preset()->security()->ignoring('md5');
  3. Enforce documentation and coding standards in architecture tests

    Pest PHP provides expectations to ensure code quality and standards across namespaces, including checking for documented methods/properties, strict type declarations, and strict equality operators. The toHaveSuspiciousCharacters() expectation requires the intl PHP extension.
    arch('app')
        ->expect('App')
        ->toHaveMethodsDocumented()
        ->toHavePropertiesDocumented()
        ->toUseStrictTypes()
        ->toUseStrictEquality();
    
    arch('app')
        ->expect('App
        ->not->toHaveSuspiciousCharacters();
  4. Use wildcards in architecture expectations

    Since Pest 3.8, you can use wildcards (*) within the expect() method to match multiple namespaces. A single asterisk matches one level, while double asterisks match multiple nested levels.
    // Match all code within any 'Traits' subdirectory (e.g., App\Models\Traits)
    arch()
        ->expect('App\*\Traits')
        ->toBeTraits();
    
    // Match all code within any 'Traits' namespace regardless of depth (e.g., App\A\B\Traits)
    arch()
        ->expect('App\*\*\Traits')
        ->toBeTraits();
  5. Validate Enum types in Architecture Testing

    Use these expectations to enforce specific Enum types within a namespace:

    • toBeEnums(): Ensures all files in the namespace are enums.
    • toBeIntBackedEnums(): Ensures all enums in the namespace are backed by integers.
    • toBeStringBackedEnums(): Ensures all enums in the namespace are backed by strings.
    arch('app')
        ->expect('App\Enums')
        ->toBeIntBackedEnums();
  6. Create and use custom architecture presets

    Custom presets allow you to group a set of architectural expectations that can be reused across multiple projects or provided via a plugin. You define them using pest()->presets()->custom() and invoke them via arch()->preset()->{name}().
    // Define a custom preset named 'ddd'
    pest()->presets()->custom('ddd', function () {
        return [
            expect('Infrastructure')->toOnlyBeUsedIn('Application'),
            expect('Domain')->toOnlyBeUsedIn('Application'),
        ];
    });
    
    // Define a preset using the application's PSR-4 namespaces
    pest()->presets()->custom('silex', function (array $userNamespaces) {
        return [
            expect($userNamespaces)->toBeArray(),
        ];
    });
    
    // Use the custom preset in a test
    arch()->preset()->silex();
  7. Refine architecture expectations with modifiers

    Modifiers allow you to narrow the scope of an architecture expectation to specific types of code (classes, enums, etc.) or filter by inheritance and trait usage.
    // Restrict to classes only
    arch('app')->expect('App')->classes()->toBeFinal();
    
    // Restrict to enums only
    arch('app')->expect('App\Models')->enums()->toOnlyBeUsedIn('App\Models');
    
    // Restrict to interfaces only
    arch('app')->expect('App')->interfaces()->toExtend('App\Contracts\Contract');
    
    // Restrict to traits only
    arch('app')->expect('App')->traits()->toExtend('App\Traits\Trait');
    
    // Restrict to abstract classes only
    arch('app')->expect('App')->abstracts()->toImplement(JsonSerializable::class);
    
    // Restrict to classes extending a specific class
    arch('app')->expect('App')->extending(Model::class)->toUseTrait(HasFactory::class);
    
    // Restrict to classes implementing a specific interface
    arch('app')->expect('App')->implementing(ShouldQueue::class)->toUseTrait(Dispatchable::class);
    
    // Restrict to classes using a specific trait
    arch('app')->expect('App')->using(HasFactory::class)->toExtend(Model::class);
  8. Understand the Pest test directory structure

    After installing Pest, the project follows a specific directory structure for tests. The tests folder is the main directory, containing Unit and Feature sub-folders for respective test types. TestCase.php is used for common functionality or setup across tests, and Pest.php is used for test suite configuration. A phpunit.xml file in the root configures PHPUnit options, which also apply to Pest since Pest is built on top of PHPUnit. Test files should typically be suffixed with Test.php (e.g., ExampleTest.php).
    ├── 📂 tests
    │   ├── 📂 Unit
    │   │   └── ExampleTest.php
    │   └── 📂 Feature
    │   │   └── ExampleTest.php
    │   └── TestCase.php
    │   └── Pest.php
    ├── phpunit.xml
  9. Exclude code from architecture tests using ignoring()

    The ignoring() method excludes specific namespaces, classes, or functions from a rule. To globally ignore namespaces (like treating a framework as native code rather than a dependency), use ignore() within a beforeEach() block.
    // Ignore a specific function in a preset
    arch()->preset()->php()->ignoring('die');
    
    // Ignore a specific namespace for a specific expectation
    arch()
        ->expect('Illuminate\Support\Facades')
        ->not->toBeUsed()
        ->ignoring('App\Providers');
    
    // Global configuration to ignore namespaces and global functions
    // tests/Pest.php
    pest()->beforeEach(function () {
        $this->arch()->ignore([
            'Illuminate',
        ])->ignoreGlobalFunctions();
    });
  10. Apply predefined architecture presets

    Pest provides several built-in architecture presets to quickly enforce common standards without writing individual expectations. These presets are applied using the arch()->preset() method.
    // Enforces general PHP best practices (avoids die, var_dump, deprecated functions). Requires intl extension.
    arch()->preset()->php();
    
    // Prevents usage of security-vulnerable functions like eval() and md5().
    arch()->preset()->security();
    
    // Enforces Laravel conventions (e.g., Controller naming and public method restrictions).
    arch()->preset()->laravel();
    
    // Enforces strict types and final classes.
    arch()->preset()->strict();
    
    // Ensures strict types are NOT used and classes are NOT final.
    arch()->preset()->preset()->relaxed();