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.
Start by installing the package as a dev dependency:
composer require --dev phpstan/phpstan-phpunit
If you use phpstan/extension-installer, it autodiscoveres and loads the extension automatically. Otherwise, manually include it in your PHPStan config (phpstan.neon):
includes:
- vendor/phpstan/phpstan-phpunit/extension.neon
For stricter PHPUnit-specific checks (e.g., enforcing assertTrue() over assertSame(true)), also include:
includes:
- vendor/phpstan/phpstan-phpunit/rules.neon
The first real-world use case is running PHPStan on a test file that uses createMock()—you’ll immediately see correct type inference for mocks, including both the real class methods and mock-specific methods like expects() or method().
Mock typing: Use native intersection types (Foo&\PHPUnit\Framework\MockObject\MockObject) or @return Foo&\PHPUnit\Framework\MockObject\MockObject in PHPDocs for methods returning mocks. This preserves access to both the mock API and the real class methods.
Late-stage mocking: After configuring a mock, reassign it to a property or variable typed only as Foo—PHPStan will now disallow further calls to expects()/method() (as expected), catching accidental misuse.
Type narrowing in assertions: assertInstanceOf(SomeClass::class, $var) narrows $var to SomeClass; assertTrue($isFoo) narrows to bool(true); assertNull($value) narrows to null. This works especially well at PHPStan level 4+, catching redundant/always-true assertions.
Auto-fixable rules: Enable AssertEqualsIsDiscouragedRule, AssertSameBooleanExpectedRule, and AssertSameNullExpectedRule to auto-fix violations (e.g., assertSame(true, $x) → assertTrue($x)). Run PHPStan with --auto-fix.
DataProvider validation: With recent versions, DataProviderDataRule catches invalid or inconsistent data providers—e.g., mismatches between declared return types and actual values passed.
Intersection vs union type syntax matters: Foo|\PHPUnit\Framework\MockObject\MockObject is interpreted as a union (legacy behavior); only Foo&\PHPUnit\Framework\MockObject\MockObject gives the precise intersection. PHPStan warns if you use MockObject incorrectly as a standalone return type.
Mocking abstract classes: getMockForAbstractClass() returns a stub, not a strict mock—ensure you configure all abstract methods; PHPStan won’t complain about missing setups, but PHPUnit will at runtime.
assertArrayHasKey() limitations: In older PHPUnit versions, this rule may misbehave if keys are dynamic. Verify the key type is known (e.g., from array_keys() or array_column()).
Data providers must not use named args: PHPUnit 9/10 ignore named arguments in data providers; PHPStan’s phpstan-phpunit now flags this (phpunit.dataProviderStatic rule auto-fixes by turning arrays into static arrays).
@covers validation: Using @covers SomeInterface triggers a warning (interfaces can’t be covered). Always use concrete classes or fully qualified class names.
Auto-fix caution: Auto-fixes for assertEquals() → assertSame() are safe only when types are known identical. Double-check changes, especially with loose comparisons (e.g., 0 == '0').
Extensibility: You can extend rule behavior by creating custom TypeSpecifyingExtension or MockObjectMethodCallRule classes and registering them manually in your PHPStan config.
How can I help you explore Laravel packages today?