psalm/plugin-phpunit
Psalm plugin that teaches Psalm about PHPUnit tests. Adds better type inference and assertions for PHPUnit APIs so your test suite is analyzed more accurately. Requires Psalm v4+. Install via Composer and enable with psalm-plugin.
#[Test], #[DataProvider], #[Before]), critical for modern Laravel testing (e.g., Laravel’s #[Test] in phpunit:10).assertSame(), assertInstanceOf()), improving accuracy for Laravel’s Eloquent, API responses, and service container tests.composer require --dev psalm/plugin-phpunit
vendor/bin/psalm-plugin enable psalm/plugin-phpunit
psalm.xml (add <dir>tests/</dir> to <file-list>). No Laravel-specific overrides needed.phpunit/phpunit package (v10+) is fully supported../vendor/bin/psalm).| Risk Area | Assessment | Mitigation |
|---|---|---|
| Psalm Version Lock | Plugin drops support for Psalm 4 (requires v5+). Laravel’s ecosystem is migrating to v5+, but legacy projects may lag. | Audit composer.json for Psalm version; upgrade if <v5. Use --strict flag to enforce compatibility. |
| False Positives/Negatives | Early adopters report reduced false positives for assertions, but complex Laravel patterns (e.g., dynamic data providers) may trigger edge cases. | Start with --no-cache in CI to surface issues early. Use psalm.xml to exclude problematic files temporarily (e.g., <exclude-files>vendor/</exclude-files>). |
| Attribute Support Gaps | While #[Test], #[DataProvider] are covered, Laravel-specific attributes (e.g., #[Laravel\Tests\CreatesApplication]) may not be recognized. |
Extend plugin via Psalm’s plugin API if needed (low priority; most Laravel tests use standard PHPUnit attributes). |
| Performance Overhead | Static analysis adds ~10–30% slower test suite initialization (Psalm’s baseline). | Cache results in CI (--cache-globals) and run in parallel with PHPUnit. Exclude large test files (e.g., Feature/*Test.php) if performance is critical. |
| Breaking Changes | Plugin drops PHPUnit 7.5 support (irrelevant for Laravel) but may introduce Psalm v7-specific behaviors. | Monitor changelog for Psalm version bumps; test against psalm/psalm:dev for early warnings. |
./vendor/bin/psalm --init)?assertDatabaseHas)? Higher complexity = greater plugin value.RefreshDatabase) that may conflict?Warning: Argument 1 of assertSame() must be of type int, string given (e.g., $user->id is string but asserted as int).Warning: Data provider must return iterable, array returned (e.g., missing yield in provider).composer require --dev with version constraints (e.g., ^0.20).#[Test]) fully supported.- name: Psalm (Tests)
run: ./vendor/bin/psalm --init --no-cache --output-format=github
| Phase | Action | Tools/Commands |
|---|---|---|
| Assessment | Audit Psalm/PHPUnit versions and test complexity. | composer show psalm/phpunit grep -r "#[Test]" tests/ |
| Installation | Add plugin to composer.json and enable it. |
composer require --dev psalm/plugin-phpunit vendor/bin/psalm-plugin enable psalm/plugin-phpunit |
| Configuration | Update psalm.xml to include test files and configure strictness. |
```xml |
<file-list>
<dir>app/</dir>
<dir>tests/</dir> <!-- Add test directory -->
</file-list>
<plugins>
<plugin-class>PsalmPlugin\PHPUnitPlugin</plugin-class>
</plugins>
``` |
| Validation | Run Psalm on a subset of tests (e.g., tests/Unit/) to validate warnings. | ./vendor/bin/psalm --init --no-cache --tests-only |
| CI Integration| Add Psalm to CI pipeline before PHPUnit. | GitHub Actions example: psalm-ci-template |
| Iteration | Fix critical warnings (e.g., assertion type mismatches) and refine psalm.xml exclusions. | ./vendor/bin/psalm --init --fix (experimental) Adjust <exclude-files> as needed. |
assertDatabaseHas(), assertModelExists(), etc.assertJson()/assertJsonStructure() against actual response types.bind()/singleton() assertions.@psalm-suppress for complex generators (e.g., yield from).tests/Unit/ first (lower complexity, higher ROI).assertSame($user->id, 1) where $user->id is string.How can I help you explore Laravel packages today?