mrpunyapal/rector-pest
Rector rules for migrating PHP tests to Pest. Automates converting PHPUnit-style tests and assertions into Pest’s fluent syntax, helping you modernize test suites quickly and consistently with minimal manual edits.
composer require --dev mrpunyapal/rector-pest
rector.php:
use RectorPest\Set\PestSetList;
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([__DIR__ . '/tests'])
->withSets([PestSetList::PEST_CODE_QUALITY]);
vendor/bin/rector process --dry-run
vendor/bin/rector process
Convert verbose expect() calls to chained assertions for cleaner tests:
// Before
expect($user)->toBeInstanceOf(User::class);
expect($user->name)->toBe('John');
expect($user->email)->toBe('john@example.com');
// After (with PEST_CHAIN)
expect($user)
->toBeInstanceOf(User::class)
->name->toBe('John')
->email->toBe('john@example.com');
Version Upgrades:
Use PestLevelSetList to migrate between major Pest versions:
// rector.php
return RectorConfig::configure()
->withSets([PestLevelSetList::UP_TO_PEST_40]);
Selective Rule Application: Target specific test files or directories:
->withPaths([
__DIR__ . '/tests/Unit',
__DIR__ . '/tests/Feature',
]);
PHPUnit-to-Pest Migration:
Apply PEST_MIGRATION incrementally:
->withSets([PestSetList::PEST_MIGRATION]);
dry-run first to review changes (e.g., assertEquals() → expect()->toEqual()).Browser Testing Optimization:
For pestphp/pest-plugin-browser, use PEST_BROWSER:
->withPaths([__DIR__ . '/tests/Browser'])
->withSets([PestSetList::PEST_BROWSER]);
expect($page->value()) to $page->assertValue().Custom Rule Chaining: Combine sets for layered improvements:
->withSets([
PestSetList::PEST_CODE_QUALITY, // Readability
PestSetList::PEST_CHAIN, // Chaining
PestSetList::PEST_LARAVEL, // Laravel-specific (e.g., `Str::` → matchers)
]);
rector process --dry-run in CI to block breaking changes.- name: Run Rector
run: vendor/bin/rector process --dry-run
False Positives in Chaining:
PEST_CHAIN may merge unrelated expect() calls if variables are reused.->withRules([ChainExpectCallsRector::class])
->withExcludedPaths([__DIR__ . '/tests/EdgeCases']);
Laravel-Specific Rules:
PEST_LARAVEL requires illuminate/support. If missing, Rector will fail silently.composer require illuminate/support or exclude the set.Browser Plugin Dependencies:
PEST_BROWSER rules only work if pestphp/pest-plugin-browser is installed.composer.json before running.Semantic Ambiguity:
ConvertAssertToExpectRector may misinterpret custom assertions.Formatting Quirks:
2.4.1+.Dry-Run First:
vendor/bin/rector process --dry-run --format=diff
Verbose Logging:
vendor/bin/rector process --verbose
Rule-Specific Debugging:
Use --rules to isolate issues:
vendor/bin/rector process --rules=ChainExpectCallsRector
Custom Rules: Extend the package by creating your own Rector rules (e.g., for project-specific assertions):
use Rector\Core\Contract\Rector\RectorInterface;
class CustomPestRector implements RectorInterface { ... }
rector.php:
->withRules([CustomPestRector::class]);
Semantic Interop: Leverage the semantic registry to add custom diagnostics:
// Example: Add a new issue type
$semanticRegistry->addIssue('CustomPestIssue', [
'description' => 'Custom Pest anti-pattern',
'fix' => fn($node) => $node->setMethod('customAssert'),
]);
Conditional Rule Application:
Use Rector’s Condition to apply rules selectively:
use Rector\Core\Contract\Rector\Condition\ConditionInterface;
class OnlyInUnitTestsCondition implements ConditionInterface { ... }
->withRules([
ChainExpectCallsRector::class => [OnlyInUnitTestsCondition::class],
]);
->withExcludedPaths([__DIR__ . '/tests/Performance/*']);
--parallel for large codebases:
vendor/bin/rector process --parallel
How can I help you explore Laravel packages today?