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

Phpstan Phpunit Laravel Package

phpstan/phpstan-phpunit

PHPStan extension for PHPUnit: improves type inference for mocks (intersection types for createMock/getMock), understands Foo|MockObject phpDocs, adds early-terminating methods to avoid undefined vars, and refines assert() types. Optional strict rules catch improper assertion usage.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require --dev phpstan/phpstan-phpunit
    

    Use phpstan/extension-installer to auto-include the extension in your phpstan.neon config.

  2. Basic Configuration (if not using extension-installer):

    includes:
        - vendor/phpstan/phpstan-phpunit/extension.neon
        - vendor/phpstan/phpstan-phpunit/rules.neon  # For framework-specific rules
    
  3. First Use Case: Run PHPStan on your test suite:

    vendor/bin/phpstan analyse tests
    

    The extension will now:

    • Correctly type mock objects (e.g., Foo&\PHPUnit\Framework\MockObject\MockObject).
    • Validate assert* method usage (e.g., assertTrue(true) will flag as redundant).

Implementation Patterns

1. Mock Object Typing

Pattern: Use intersection types for mocks to enable static analysis of both mock methods (expects(), method()) and mocked class methods.

// Correct: Intersection type (PHP 8+)
private function createMock(): User&\PHPUnit\Framework\MockObject\MockObject {
    return $this->createMock(User::class);
}

// Fallback (PHP < 8 or PHPDoc)
/**
 * @return User&\PHPUnit\Framework\MockObject\MockObject
 */
private function createMock(): User {
    return $this->createMock(User::class);
}

Workflow:

  • Active Mocks: Use intersection types when the mock is still being configured (e.g., willReturn(), expects()).
  • Passive Mocks: Narrow to the mocked class type once configuration is complete (e.g., private User $user).

2. Assertion Validation

Pattern: Leverage PHPStan’s level 4 to catch redundant or incorrect assertions.

// Flags as redundant (use `assertTrue()` instead)
$this->assertSame(true, true);

// Flags as incorrect (use `assertNull()` instead)
$this->assertSame(null, null);

Integration Tips:

  • Enable strict rules in phpstan.neon:
    level: 4
    rules:
        PHPUnit\Framework\TestCase:
            assertSameBooleanExpectedRule: true
            assertSameNullExpectedRule: true
    
  • Use assertEquals()/assertNotEquals() sparingly (prefer assertSame() for identical types).

3. Data Providers

Pattern: Annotate data provider methods with @phpstan-data-provider to avoid false positives.

/**
 * @phpstan-data-provider provideTestData
 */
public function testWithData(array $input, string $expected): void {
    $this->assertEquals($expected, $this->service->process($input));
}

/**
 * @return array<int, array{0: array<int, mixed>, 1: string}>
 */
public static function provideTestData(): array {
    return [[[1, 2], "1,2"], [[3], "3"]];
}

Workflow:

  • PHPStan will validate return types of data providers (e.g., array<int, array{...}>).
  • Use Iterator, Generator, or IteratorAggregate types for dynamic providers.

4. Dynamic Method Calls

Pattern: Ignore dynamic calls to assert* methods when needed (e.g., in factories or dynamic test builders).

// In phpstan.neon
services:
    DynamicCallToAssertionIgnoreExtension:
        ignoredMethods:
            - 'buildAssertion'

Gotchas and Tips

Pitfalls

  1. Mock Configuration Timing:

    • Gotcha: Forgetting to use intersection types when mocks are still being configured leads to "method not found" errors (e.g., expects()).
    • Fix: Always use Class&\PHPUnit\Framework\MockObject\MockObject for active mocks.
  2. Data Provider Return Types:

    • Gotcha: PHPStan may misinterpret data provider return types (e.g., array<int, mixed> vs. array<int, array{...}>).
    • Fix: Explicitly type data providers with @return array<int, array{...}> or use @phpstan-data-provider.
  3. Strict Rules Overhead:

    • Gotcha: Enabling all strict rules (e.g., assertSameBooleanExpectedRule) may flag legitimate use cases.
    • Fix: Disable rules selectively in phpstan.neon:
      rules:
          PHPUnit\Framework\TestCase:
              assertSameBooleanExpectedRule: false
      
  4. PHPUnit Version Mismatches:

    • Gotcha: Rules may fail if your PHPUnit version isn’t supported (e.g., PHPUnit 12+ attributes).
    • Fix: Check release notes for compatibility.

Debugging Tips

  1. False Positives:

    • Use @var or @phpstan-ignore-next-line to suppress specific errors:
      /** @var User&\PHPUnit\Framework\MockObject\MockObject */
      $mock = $this->createMock(User::class);
      
  2. Performance:

    • Exclude slow tests from analysis:
      analysePaths:
          - tests/Unit/
          - !tests/Integration/SlowTests.php
      
  3. Auto-Fixes:

    • Enable auto-fixing for common issues:
      vendor/bin/phpstan analyse --generate-baseline tests --level 4
      vendor/bin/phpstan analyse tests --fix
      

Extension Points

  1. Custom Assertions:

    • Extend DynamicCallToAssertionIgnoreExtension to ignore custom assertion methods:
      services:
          DynamicCallToAssertionIgnoreExtension:
              ignoredMethods:
                  - 'customAssert*'
      
  2. Mock Builders:

    • Support createMockForIntersectionOfInterfaces (PHPUnit 9.5+):
      $mock = $this->createMockForIntersectionOfInterfaces([User::class, Logger::class]);
      
  3. PHPDoc Overrides:

    • Use @method to add mock methods to PHPDoc:
      /**
       * @method void expects()
       * @method $this method(string $name, callable $mock)
       */
      private function createMock(): User&\PHPUnit\Framework\MockObject\MockObject { ... }
      
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.
calmfox/watch-sylius
damienfern/grpc-symfony-bundle
atoolo/index-bundle
atoolo/genai-bundle
coprotoai/laravel-ticket
davidjln/llm-carbon-bundle
cryonighter/valid-request-bundle
coolms/taxonomy-bundle
coolms/field-bundle
articulate-orm/symfony
aaix/laravel-tall-architect
ephoto/akeneo-connector
emmanuelballery/eb-plantumlbundle
emielburgman/symfony-visitor-beacon
emielburgman/symfony-visit-storage
emielburgman/symfony-security-headers
emielburgman/symfony-log-viewer
emarref/xdebug-bundle
emarref/pubnub-bundle
elriseio/finance-money-bundle