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

Plugin Phpunit Laravel Package

psalm/plugin-phpunit

A Psalm plugin for PHPUnit that improves static analysis of test suites by teaching Psalm about PHPUnit-specific APIs, assertions, and mocking. Install with Composer, enable via psalm-plugin, and get more accurate type checking in your tests. Requires Psalm v4 or later.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Install the plugin in your Laravel project:
    composer require --dev psalm/plugin-phpunit
    
  2. Enable the plugin for Psalm:
    vendor/bin/psalm-plugin enable psalm/plugin-phpunit
    
  3. Configure Psalm to analyze test files: Update psalm.xml to include your test directory:
    <file-list>
        <dir>tests/</dir>
    </file-list>
    
  4. Run Psalm with the plugin:
    ./vendor/bin/psalm --init
    

First Use Case: Analyzing a Test Class

Run Psalm on your test files to immediately see improvements:

./vendor/bin/psalm --no-cache

Example Output: Psalm will now understand PHPUnit assertions (e.g., assertSame(), assertInstanceOf()) and validate their types, reducing false positives in your tests.


Implementation Patterns

Core Workflows

  1. Type Validation for Assertions Psalm will analyze assertion methods (e.g., assertEquals(), assertInstanceOf()) and validate their arguments. For example:

    public function testUserIdIsInteger(): void
    {
        $user = User::factory()->create();
        $this->assertSame(1, $user->id); // Psalm checks if `$user->id` is an int
    }
    
    • Pattern: Use assertSame()/assertEquals() with typed arguments to leverage Psalm’s type inference.
  2. Data Provider Analysis Psalm validates @dataProvider methods and their return types:

    #[DataProvider('integerProvider')]
    public function testIntegerHandling(int $value): void
    {
        $this->assertIsInt($value);
    }
    
    public function integerProvider(): iterable
    {
        yield [1];
        yield [2];
    }
    
    • Pattern: Ensure data providers return iterable or array types. Psalm will flag mismatches (e.g., returning a scalar).
  3. Attribute Support Psalm understands PHPUnit 9+ attributes (#[Test], #[DataProvider], #[Before]):

    #[Test]
    public function testAttributeSupport(): void
    {
        $this->assertTrue(true);
    }
    
    • Pattern: Migrate from annotations (@test) to attributes for full Psalm support.
  4. Mocking and Stubs Psalm validates mock interactions (e.g., createMock(), getMockBuilder()):

    $mock = $this->createMock(UserRepository::class);
    $mock->method('find')->willReturn(new User());
    
    • Pattern: Use createMock() with generic types (e.g., createMock(UserRepository::class)) for better type hints.

Integration Tips

  • Laravel-Specific Use Cases:

    • Eloquent Tests: Validate queries return expected types (e.g., Collection vs. Builder):
      $users = User::query()->where('active', true)->get();
      $this->assertInstanceOf(Collection::class, $users);
      
    • API Tests: Ensure JSON responses match asserted types:
      $response = $this->get('/api/users');
      $this->assertJsonStructure([['id', 'name']]);
      
    • Service Container: Validate bindings in tests:
      $this->app->bind(UserRepository::class, fn () => new UserRepository());
      $this->assertTrue($this->app->has(UserRepository::class));
      
  • CI/CD Pipeline: Add Psalm to your test suite in phpunit.xml:

    <listeners>
        <listener class="Psalm\Plugin\PHPUnit\Listener" />
    </listeners>
    

    Run Psalm before PHPUnit to fail fast on type errors:

    ./vendor/bin/psalm --no-cache && ./vendor/bin/phpunit
    
  • Configuration: Customize Psalm’s behavior in psalm.xml:

    <plugin_class>Psalm\Plugin\PHPUnit\Plugin</plugin_class>
    <param name="checkForThrowsDocblock">true</param> <!-- Validate `@throws` annotations -->
    

Gotchas and Tips

Pitfalls

  1. False Positives with Dynamic Types: Psalm may flag assertions involving dynamic types (e.g., mixed or array) as errors. Use @psalm-suppress sparingly:

    #[psalm-suppress MixedArgument]
    $this->assertEquals($dynamicValue, 'expected');
    
  2. Data Provider Mismatches: If a data provider returns a tuple but the test expects separate arguments, Psalm will report an error. Ensure consistency:

    // Correct: Data provider returns iterable of arrays
    public function provider(): iterable
    {
        yield [1, 'one'];
        yield [2, 'two'];
    }
    
  3. Legacy PHPUnit Annotations: The plugin prioritizes attributes (#[Test]) over annotations (@test). Update old tests to avoid warnings.

  4. Mock Method Signatures: Psalm may not infer mock method return types if they’re not explicitly set. Use willReturn() with typed values:

    $mock->method('getName')->willReturn('John'); // Psalm infers `string`
    
  5. Performance Overhead: Analyzing large test suites may slow down Psalm. Use --no-cache in CI and cache results locally:

    ./vendor/bin/psalm --init && ./vendor/bin/psalm --no-cache
    

Debugging Tips

  • Enable Verbose Output: Run Psalm with -v to debug plugin behavior:
    ./vendor/bin/psalm -v
    
  • Isolate Test Files: Analyze a single test file to pinpoint issues:
    ./vendor/bin/psalm tests/Feature/UserTest.php
    
  • Check Plugin Compatibility: Ensure your Psalm version matches the plugin’s requirements (e.g., Psalm v7+ for recent releases). Update via:
    composer require psalm/psalm:^7.0 --dev
    

Extension Points

  1. Custom Assertions: Extend the plugin to support custom assertions by defining stubs in psalm.xml:

    <stub_files>
        <file>stubs/CustomAssertions.php</file>
    </stub_files>
    

    Example stub:

    namespace PHPUnit\Framework\Assert {
        function assertCustomType(mixed $actual, string $expectedType): void { /* ... */ }
    }
    
  2. Data Provider Enhancements: Add support for complex data providers (e.g., generators) by extending the plugin’s ProviderAnalyzer class.

  3. Laravel-Specific Stubs: Create stubs for Laravel’s testing helpers (e.g., createMock(), JsonResponse) to improve type inference:

    namespace Illuminate\Testing {
        class JsonResponse {
            public function assertJsonStructure(array $structure): void { /* ... */ }
        }
    }
    

Configuration Quirks

  • Excluding Files: Use <exclude-files> in psalm.xml to skip problematic test files:
    <exclude-files>
        <file>tests/Integration/SlowTests.php</file>
    </exclude-files>
    
  • Strict Mode: Enable strict_types=1 in test files to catch more type errors:
    <?php declare(strict_types=1);
    
  • Plugin Order: Ensure psalm/plugin-phpunit is loaded after core Psalm plugins in psalm.xml:
    <plugins>
        <plugin_class>Psalm\Plugin\PHPUnit\Plugin</plugin_class>
    </plugins>
    
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.
takashato/laravel-vietqr
aitor24/laralang
kraenzle-ritter/entity-relations
dindigital/din-image
pomirleanu/image-colors
sawmainek/laravel-api-generator
delatbabel/jsonresponses
mouhamedfd/permissions-generator
eduplus/qr
laravel2580/idehelpers
delatbabel/viewpages
anavel/gettext
muhamadrezaar/highcharts
delatbabel/keylists
frankkessler/salesforce-laravel-oauth2-rest
anavel/foundation
wdmtech/enviricons
eii/laravel-captcha
muka/shape-reader
tdt/json