phpyh/psalm-tester
Run Psalm assertions from .phpt files. Write phpt tests with --FILE-- and --EXPECT/EXPECTF-- for traces and errors, then execute them via a PHPUnit test suite using PsalmTester. Supports per-suite default Psalm args and per-test --ARGS-- overrides.
Installation
composer require --dev phpyh/psalm-tester
Ensure psalm is installed globally or via Composer (composer require --dev vimeo/psalm).
Basic Configuration
Add to composer.json under extra:
"psalm": {
"bin": "vendor/bin/psalm",
"config": "psalm.xml"
}
Or configure via PsalmTester::setPsalmConfig() in PHP.
First Use Case Run a simple test to verify Psalm’s output:
use Phyh\PsalmTester\PsalmTester;
test('Psalm passes with no errors', function () {
$result = PsalmTester::run();
$result->assertNoErrors();
});
tests/PsalmTest.php (if creating custom assertions)config/psalm.xml (Psalm’s core config)phpunit.xml (if integrating with PHPUnit)Use in GitHub Actions/GitLab CI to enforce Psalm compliance:
# .github/workflows/psalm.yml
jobs:
psalm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: composer install
- run: vendor/bin/phpunit --testdox-html --filter "Psalm"
Assert expected errors for new/changed code:
test('New method triggers expected Psalm error', function () {
$result = PsalmTester::run(['--init']);
$result->assertErrorContains('ClassMyClass has a method with no return type');
});
Compare against a snapshot of "known good" output:
test('Psalm output matches baseline', function () {
$result = PsalmTester::run();
$result->assertOutputMatchesFile(__DIR__.'/baseline.txt');
});
PsalmTester::setPsalmConfig(app()->basePath('psalm.xml'));
use Illuminate\Console\Command;
use Phyh\PsalmTester\PsalmTester;
class RunPsalmCommand extends Command {
protected $signature = 'psalm:run';
public function handle() {
$result = PsalmTester::run();
$this->line($result->getOutput());
if ($result->hasErrors()) {
$this->error('Psalm found issues!');
}
}
}
pest or phpunit: Use PsalmTester in feature tests to validate type safety.PsalmTester::run(['--threads', '--init', 'src/']);
PsalmTesterResult for project-specific checks:
$result->assertErrorCount(3); // Custom assertion
False Positives in CI
vendor/ paths).--init to regenerate cache or mock vendor/ in tests:
PsalmTester::run(['--init', '--no-cache']);
Slow Test Suites
--threads:
vendor/bin/psalm --threads=4 --init
Baseline Drift
vendor/bin/psalm --init > baseline.txt
Laravel-Specific Quirks
Illuminate\Support\Collection).ignoreErrors in psalm.xml:
<error-level>Errors</error-level>
<ignoreErrors>
<error>MixedAssignment</error>
<error>MixedReturnType</error>
</ignoreErrors>
PsalmTester::run(['--debug']);
PsalmTester::run(['--no-cache', '--init']);
PsalmTester::run(['src/MyClass.php']);
Custom Error Parsing
Override PsalmTesterResult::parseOutput() to handle project-specific Psalm formats.
Pre/Post-Run Hooks
Extend PsalmTester to run setup/teardown:
PsalmTester::beforeRun(function () {
// Copy config files, etc.
});
Git Pre-Commit Hooks
Use PsalmTester to block commits with Psalm errors:
# .git/hooks/pre-commit
vendor/bin/phpunit --filter "Psalm" || exit 1
Visual Studio Code Integration
Add a task to run Psalm via PsalmTester in tasks.json:
{
"label": "Run Psalm",
"type": "process",
"command": "vendor/bin/phpunit --filter Psalm",
"problemMatcher": ["$phpunit"]
}
How can I help you explore Laravel packages today?