mrpunyapal/peststan
PestStan integrates PHPStan with the Pest testing framework, making static analysis fit naturally into your test workflow. Adds Pest-friendly configuration and helpers so you can run PHPStan on your codebase with minimal setup.
Installation:
composer require --dev mrpunyapal/peststan
Ensure phpstan/extension-installer is installed (recommended) or manually add to phpstan.neon:
includes:
- vendor/mrpunyapal/peststan/extension.neon
First Use Case: Run PHPStan on your test directory:
vendor/bin/phpstan analyse tests
PestStan will automatically detect your Pest.php configuration and provide type hints for expect() and $this in test closures.
Type-Safe Expectations:
// tests/Feature/ExampleTest.php
it('validates string input', function () {
expect('hello')->toBeString(); // PHPStan knows this is type-safe
});
TestCase Integration:
// tests/Pest.php
uses(Tests\TestCase::class)->in('Feature');
// tests/Feature/ExampleTest.php
it('uses TestCase methods', function () {
$this->assertDatabaseHas('users', ['email' => 'test@example.com']);
// PHPStan knows $this is Tests\TestCase
});
Dynamic Property Inference:
beforeEach(function () {
$this->user = User::factory()->create(); // PHPStan infers User type
});
it('uses inferred property', function () {
$this->user->name; // No mixed-type errors
});
Assertion Chaining with Type Narrowing:
/** @var int|string $value */
$value = getValue();
expect($value)
->toBeString() // PHPStan now knows $value is string
->toStartWith('test');
Custom TestCase Methods:
// tests/Pest.php
uses(CustomTestCase::class)->in('Unit');
// tests/Unit/ExampleTest.php
it('uses custom helper')->customHelper();
Architecture Testing:
expect('App\Models')
->toExtend('Illuminate\Database\Eloquent\Model')
->ignoring('App\Models\Legacy');
Add PHPStan to your test suite in phpunit.xml or composer.json:
<phpunit>
<extensions>
<extension class="PHPStan\Extensions\PHPUnit\PHPUnitExtension"/>
</extensions>
</phpunit>
Run in CI:
composer test
Static Closures:
it('fails', static function () { // ❌ Error: static closure breaks $this binding
$this->assertTrue(true);
});
Fix: Remove static or use beforeEach for setup.
Missing Property Inference:
beforeEach(function () {
$this->user = User::factory()->create(); // ❌ PHPStan may not infer type
});
Fix: Add @var annotation:
/** @var User $user */
$user = User::factory()->create();
$this->user = $user;
Incorrect throws() Usage:
it('fails')->throws(stdClass::class); // ❌ Error: stdClass is not Throwable
Fix: Use a valid Throwable class.
Enable Verbose Output:
vendor/bin/phpstan analyse tests --verbose
Helps diagnose misconfigured Pest.php paths.
Manual TestCase Override:
If auto-detection fails, force it in phpstan.neon:
parameters:
peststan:
testCaseClass: App\Testing\TestCase
Ignoring Rules:
Suppress specific rules in phpstan.neon:
parameters:
ignoreErrors:
- identifier: pest.test.emptyClosure
Custom Rules:
Extend PestDiagnosticIdentifiers to add new static analysis rules.
Custom TestCase Methods:
Ensure your TestCase class is properly annotated or referenced in Pest.php.
Pest Version Compatibility:
PestStan supports Pest 3/4/5. Verify your version in composer.json:
"require-dev": {
"pestphp/pest": "^5.0"
}
paths:
- tests/Unit
- !tests/Integration/LargeTest.php
vendor/bin/phpstan analyse tests --generate-baseline
How can I help you explore Laravel packages today?