Weave Code
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

Psalm plugin that understands PHPUnit tests to improve static analysis accuracy. Adds PHPUnit-aware stubs and type inference so assertions, mocks, and test helpers are checked correctly. Requires Psalm v4+. Install via Composer and enable with psalm-plugin.

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 your Psalm configuration:
    vendor/bin/psalm-plugin enable psalm/plugin-phpunit
    
  3. Run Psalm on your test directory:
    vendor/bin/psalm --init
    vendor/bin/psalm --no-cache --output-format=github
    

First Use Case: Analyzing a Test Class

Run Psalm on a test file with PHPUnit assertions to see immediate improvements:

vendor/bin/psalm --no-cache --output-format=github tests/Feature/ExampleTest.php

Expected Outcome: Reduced false positives in assertions (e.g., assertSame(), assertInstanceOf()) and proper handling of @dataProvider annotations.


Implementation Patterns

1. Test Class Analysis

  • Pattern: Run Psalm on entire test suites to catch:
    • Unused test methods or data providers.
    • Type mismatches in assertions (e.g., assertSame($expected, $actual) where $expected is int but $actual is string).
    • Improperly typed mocks (e.g., createMock(Service::class) with incorrect expectations).
  • Workflow:
    vendor/bin/psalm --init
    vendor/bin/psalm --no-cache --output-format=github tests/
    

2. Data Provider Validation

  • Pattern: Use @dataProvider or #[DataProvider] with complex return types (e.g., iterable<list<mixed>>).
    #[DataProvider]
    public static function provideTestData(): iterable {
        yield ['input' => 'test', 'expected' => 123];
        yield ['input' => 'data', 'expected' => 456];
    }
    
    public function testExample(string $input, int $expected): void {
        $this->assertSame($expected, someFunction($input));
    }
    
  • Psalm Behavior: Validates that:
    • The test method parameters match the provider’s return type.
    • All provider cases are used (no "unused data provider" warnings).

3. Mock and Stub Analysis

  • Pattern: Leverage Psalm’s improved understanding of PHPUnit mocks:
    public function testMockInteraction(): void {
        $mock = $this->createMock(Service::class);
        $mock->method('process')
             ->willReturn('result');
    
        $this->assertSame('result', $mock->process());
    }
    
  • Psalm Behavior: Catches:
    • Incorrect method expectations (e.g., willReturn() with wrong type).
    • Missing stubs for abstract methods in mocks.

4. Integration with Laravel-Specific Tests

  • Pattern: Use with Laravel’s testing helpers (e.g., Http::fake(), DatabaseMigrations::run()):
    public function testApiEndpoint(): void {
        Http::fake([
            'api.example.com' => Http::response('{"status": "ok"}'),
        ]);
    
        $response = $this->get('/endpoint');
        $this->assertJson(['status' => 'ok']);
    }
    
  • Psalm Behavior: Reduces false positives in:
    • assertJson() or assertSee() with dynamic responses.
    • Mock HTTP interactions (e.g., Http::fake()).

5. CI/CD Pipeline Integration

  • Pattern: Add Psalm test analysis as a pre-commit hook or CI gate:
    # .github/workflows/psalm.yml
    jobs:
      psalm:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - run: composer install
          - run: vendor/bin/psalm --no-cache --output-format=github tests/
    
  • Expected Outcome: Fail builds early if tests have type issues, reducing CI flakiness.

Gotchas and Tips

Pitfalls

  1. Psalm Version Mismatch:

    • Issue: The plugin requires Psalm v7+ (as of 0.20.0). Using older versions (e.g., 0.19.x) may cause crashes or incomplete analysis.
    • Fix: Upgrade Psalm:
      composer require --dev psalm/psalm "^7.0"
      
  2. Unsupported PHPUnit Features:

    • Issue: The plugin does not support PHPUnit 7.5 or older. If your project uses legacy PHPUnit, upgrade first:
      composer require --dev phpunit/phpunit "^9.0"
      
    • Fix: Update phpunit.xml to use PHPUnit 9+.
  3. False Positives with Dynamic Data Providers:

    • Issue: Psalm may flag iterable<mixed> return types from data providers as "non-iterable" if not properly typed.
    • Fix: Explicitly type provider return values:
      #[DataProvider]
      public static function provideData(): iterable {
          yield ['key' => 'value']; // Psalm infers `iterable<array-key, array-value>`
      }
      
  4. Sticky Data Providers:

    • Issue: Psalm may report a data provider as "unused" even after removing its @dataProvider annotation.
    • Fix: Run Psalm with --no-cache to force re-analysis:
      vendor/bin/psalm --no-cache
      
  5. Prophecy Mocks:

    • Issue: Complex Prophecy mocks (e.g., Argument::that() with closures) may trigger false positives.
    • Fix: Use @psalm-suppress for edge cases:
      $mock->method('process')
           ->willReturnCallback(fn($arg) => $arg + 1);
      // @psalm-suppress MixedArgument
      

Debugging Tips

  1. Enable Verbose Output:

    vendor/bin/psalm --verbose --no-cache
    
    • Helps diagnose plugin-specific issues (e.g., unsupported annotations).
  2. Isolate Test Files:

    • Run Psalm on a single test file to pinpoint issues:
      vendor/bin/psalm tests/Feature/ExampleTest.php
      
  3. Check Plugin Compatibility:

    • Ensure psalm/plugin-phpunit is enabled in psalm-plugin.json:
      {
        "plugins": ["psalm/plugin-phpunit"]
      }
      

Extension Points

  1. Custom Data Provider Types:

    • Extend the plugin’s type system by adding custom Psalm stubs for domain-specific providers:
      // stubs/Your/DataProviderStub.php
      namespace Your;
      
      class DataProviderStub {
          /**
           * @return iterable<array{string, int}>
           */
          public static function provideCustomData(): iterable {}
      }
      
  2. Suppress Specific Warnings:

    • Use @psalm-suppress for known false positives:
      #[Test]
      public function testEdgeCase(): void {
          // @psalm-suppress MixedReturnStatement
          $result = someUnstableFunction();
          $this->assertTrue($result);
      }
      
  3. Integrate with Laravel’s Test Helpers:

    • Add stubs for Laravel-specific testing methods (e.g., DatabaseMigrations::run()):
      // stubs/Laravel/Test/DatabaseMigrations.php
      namespace Laravel\Test;
      
      class DatabaseMigrations {
          public static function run(): void {}
      }
      

Performance Quirks

  • Large Test Suites: Psalm may slow down with thousands of test files. Use --parallel for faster analysis:
    vendor/bin/psalm --parallel --no-cache
    
  • Memory Limits: Increase PHP memory if Psalm crashes:
    php -d memory_limit=2G vendor/bin/psalm
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport