Rector
Source code: github.com/pestphp/pest-plugin-rector
Pest's Rector plugin provides automated refactoring rules powered by Rector. It helps you simplify and modernize your test code, as well as upgrade between major Pest versions.
To get started, require the plugin via Composer and install Rector:
1composer require pestphp/pest-plugin-rector --dev2composer require rector/rector --dev
Rule Sets
The plugin groups its rules into predefined sets, so that you may enable exactly the transformations you need. You may register these sets in your project's rector.php file, and combine as many of them as you wish.
Coding Style
The CODING_STYLE set rewrites raw PHP assertions into Pest's expressive, built-in matchers, and simplifies redundant expectation patterns. For example, it converts expect(count($array))->toBe(3) into expect($array)->toHaveCount(3).
It also merges consecutive expectations on the same value into a single, fluent chain, and orders type checks first within each chain, so that your assertions read from the most general to the most specific:
1use Pest\Rector\Set\PestSetList;2use Rector\Config\RectorConfig;3 4return RectorConfig::configure()5 ->withPaths([__DIR__ . '/tests'])6 ->withSets([7 PestSetList::CODING_STYLE,8 ]);
Pest Version Upgrades
To upgrade your test suite between major Pest versions, you may reach for the level sets provided by PestLevelSetList. Unlike the sets above, a level set is cumulative: it applies every migration up to and including the version you target, so that a suite on an older version arrives fully up to date in a single pass:
1use Pest\Rector\Set\PestLevelSetList;2use Rector\Config\RectorConfig;3 4// Upgrade from Pest v2 to v35return RectorConfig::configure()6 ->withPaths([__DIR__ . '/tests'])7 ->withSets([PestLevelSetList::UP_TO_PEST_30]);
To upgrade to Pest v4, target UP_TO_PEST_40 instead. As it is cumulative, it includes every transformation from UP_TO_PEST_30 as well, so you may upgrade from either Pest v2 or v3 in one step:
1use Pest\Rector\Set\PestLevelSetList;2use Rector\Config\RectorConfig;3 4return RectorConfig::configure()5 ->withPaths([__DIR__ . '/tests'])6 ->withSets([PestLevelSetList::UP_TO_PEST_40]);
Of course, if you would rather apply the migration rules for a single version in isolation, the individual PestSetList::PEST_30 and PestSetList::PEST_40 sets remain available as well.
Preview & Apply Changes
To preview changes before applying them, you may run Rector with the --dry-run flag:
1vendor/bin/rector process --dry-run
Once you are satisfied with the proposed changes, you may run Rector without the flag to apply them:
1vendor/bin/rector process
All Rules
For reference, below is every rule provided by the plugin, along with an example of the transformation it applies.
ChainExpectCallsRector
- class:
Pest\Rector\Rules\ChainExpectCallsRector
This rule chains multiple expect() calls on the same value into a single chained expectation:
1-expect($a)->toBe(10);2-expect($a)->toBeInt();3+expect($a)->toBe(10)4+ ->toBeInt();
1-expect($a)->toBe(10);2-expect($b)->toBe(10);3+expect($a)->toBe(10)4+ ->and($b)->toBe(10);
ConvertAssertToExpectRector
- class:
Pest\Rector\Rules\ConvertAssertToExpectRector
This rule converts $this->assert*() calls to Pest expect() chains:
1-$this->assertEquals('expected', $result);2-$this->assertTrue($value);3-$this->assertCount(3, $items);4+expect($result)->toEqual('expected');5+expect($value)->toBeTrue();6+expect($items)->toHaveCount(3);
ConvertBeforeAllInDescribeRector
- class:
Pest\Rector\Rules\ConvertBeforeAllInDescribeRector
This rule replaces invalid beforeAll() and afterAll() hooks inside describe() with beforeEach() and afterEach():
1 describe('users', function (): void {2- beforeAll(function (): void {3+ beforeEach(function (): void {4 refreshDatabase();5 });6 });
ConvertExpectExceptionToThrowRector
- class:
Pest\Rector\Rules\ConvertExpectExceptionToThrowRector
This rule converts $this->expectException() and $this->expectExceptionMessage() patterns to expect()->toThrow():
1-$this->expectException(RuntimeException::class);2-$this->expectExceptionMessage('error');3-doSomething();4+expect(fn () => doSomething())->toThrow(RuntimeException::class, 'error');
EnsureTypeChecksFirstRector
- class:
Pest\Rector\Rules\EnsureTypeChecksFirstRector
This rule ensures that type-check matchers, such as toBeInt() and toBeInstanceOf(), appear before value assertions in expect() chains and consecutive expectations:
1-expect($a)->toBe(10)->toBeInt();2+expect($a)->toBeInt()->toBe(10);
FixInvalidRepeatValueRector
- class:
Pest\Rector\Rules\FixInvalidRepeatValueRector
This rule normalizes invalid literal repeat() counts to 1:
1 it('retries once', function (): void {2 expect(true)->toBeTrue();3-})->repeat(0);4+})->repeat(1);
RemoveDebugExpectationsRector
- class:
Pest\Rector\Rules\RemoveDebugExpectationsRector
This rule removes debug method calls, such as dump(), dd(), and ray(), from expect() chains:
1-expect($user)->dump()->toBeInstanceOf(User::class);2+expect($user)->toBeInstanceOf(User::class);
RemoveOnlyRector
- class:
Pest\Rector\Rules\RemoveOnlyRector
This rule removes only() from all tests:
1-test()->only();2+test();
RemoveRedundantLiteralTypeExpectationRector
- class:
Pest\Rector\Rules\RemoveRedundantLiteralTypeExpectationRector
This rule removes redundant literal type expectations when a later matcher keeps the chain meaningful:
1 expect('pest')2- ->toBeString()3 ->toStartWith('p');
RemoveRedundantPestUsesRector
- class:
Pest\Rector\Rules\RemoveRedundantPestUsesRector
This rule removes redundant local Pest uses() calls that are already configured globally in tests/Pest.php:
1 // tests/Pest.php contains:2 // pest()->use(RefreshDatabase::class)->in('Feature');3 4 // tests/Feature/UserTest.php5-pest()->use(RefreshDatabase::class, SomeOtherTrait::class);6+pest()->use(SomeOtherTrait::class);
RemoveStaticTestClosureRector
- class:
Pest\Rector\Rules\RemoveStaticTestClosureRector
This rule removes the static keyword from Pest test and hook callbacks that use the test case instance:
1-it('uses the test case instance', static function (): void {2+it('uses the test case instance', function (): void {3 expect($this)->not->toBeNull();4 });
SimplifyComparisonExpectationsRector
- class:
Pest\Rector\Rules\SimplifyComparisonExpectationsRector
This rule converts expect($x > 10)->toBeTrue() to expect($x)->toBeGreaterThan(10):
1-expect($value > 10)->toBeTrue();2-expect($value >= 10)->toBeTrue();3+expect($value)->toBeGreaterThan(10);4+expect($value)->toBeGreaterThanOrEqual(10);
SimplifyExpectNotRector
- class:
Pest\Rector\Rules\SimplifyExpectNotRector
This rule simplifies negated expectations by flipping the matcher:
1-expect(!$condition)->toBeTrue();2+expect($condition)->toBeFalse();
SimplifyFilesystemMatchersRector
- class:
Pest\Rector\Rules\SimplifyFilesystemMatchersRector
This rule simplifies combined filesystem checks to single Pest matchers:
1-expect(is_file($path) && is_readable($path))->toBeTrue();2+expect($path)->toBeReadableFile();
SimplifyToBeTruthyFalsyRector
- class:
Pest\Rector\Rules\SimplifyToBeTruthyFalsyRector
This rule converts boolean cast assertions to the toBeTruthy()/toBeFalsy() matchers:
1-expect((bool) $value)->toBeTrue();2+expect($value)->toBeTruthy();
SimplifyToLiteralBooleanRector
- class:
Pest\Rector\Rules\SimplifyToLiteralBooleanRector
This rule simplifies expect($x)->toBe(true) to expect($x)->toBeTrue():
1-expect($value)->toBe(true);2-expect($value)->toBe(null);3+expect($value)->toBeTrue();4+expect($value)->toBeNull();
TapToDeferRector
- class:
Pest\Rector\Rules\Pest2ToPest3\TapToDeferRector
This rule replaces the deprecated ->tap() method with ->defer() for the Pest v3 migration:
1-expect($value)->tap(fn ($value) => dump($value))->toBe(10);2+expect($value)->defer(fn ($value) => dump($value))->toBe(10);
ToBeTrueNotFalseRector
- class:
Pest\Rector\Rules\ToBeTrueNotFalseRector
This rule simplifies double-negative expectations like ->not->toBeFalse() to ->toBeTrue():
1-expect($value)->not->toBeFalse();2+expect($value)->toBeTrue();
ToHaveMethodOnClassRector
- class:
Pest\Rector\Rules\Pest2ToPest3\ToHaveMethodOnClassRector
This rule changes expect($object)->toHaveMethod() to expect($object::class)->toHaveMethod() for Pest v3:
1-expect($user)->toHaveMethod('getName');2+expect($user::class)->toHaveMethod('getName');
UseEachModifierRector
- class:
Pest\Rector\Rules\UseEachModifierRector
This rule converts foreach loops with expect() calls to use the ->each modifier:
1-foreach ($items as $item) {2- expect($item)->toBeString();3-}4+expect($items)->each->toBeString();
UseInstanceOfMatcherRector
- class:
Pest\Rector\Rules\UseInstanceOfMatcherRector
This rule converts expect($obj instanceof User)->toBeTrue() to expect($obj)->toBeInstanceOf(User::class):
1-expect($user instanceof User)->toBeTrue();2+expect($user)->toBeInstanceOf(User::class);
UseSequenceMatcherRector
- class:
Pest\Rector\Rules\UseSequenceMatcherRector
This rule converts consecutive indexed expect() calls to sequence():
1-expect($items[0])->toBe('a');2-expect($items[1])->toBe('b');3+expect($items)->sequence(fn ($e) => $e->toBe('a'), fn ($e) => $e->toBe('b'));
UseStrictEqualityMatchersRector
- class:
Pest\Rector\Rules\UseStrictEqualityMatchersRector
This rule converts strict equality expressions to the toBe() matcher:
1-expect($a === $b)->toBeTrue();2+expect($a)->toBe($b);
UseToBeAlphaNumericRector
- class:
Pest\Rector\Rules\UseToBeAlphaNumericRector
This rule converts ctype_alnum() checks to the toBeAlphaNumeric() matcher:
1-expect(ctype_alnum($value))->toBeTrue();2+expect($value)->toBeAlphaNumeric();
UseToBeAlphaRector
- class:
Pest\Rector\Rules\UseToBeAlphaRector
This rule converts ctype_alpha() checks to the toBeAlpha() matcher:
1-expect(ctype_alpha($value))->toBeTrue();2+expect($value)->toBeAlpha();
UseToBeBetweenRector
- class:
Pest\Rector\Rules\UseToBeBetweenRector
This rule converts expect($value >= $min && $value <= $max)->toBeTrue() to expect($value)->toBeBetween($min, $max):
1-expect($value >= 1 && $value <= 10)->toBeTrue();2+expect($value)->toBeBetween(1, 10);
UseToBeDigitsRector
- class:
Pest\Rector\Rules\UseToBeDigitsRector
This rule converts ctype_digit() checks to the toBeDigits() matcher:
1-expect(ctype_digit($value))->toBeTrue();2+expect($value)->toBeDigits();
UseToBeDirectoryRector
- class:
Pest\Rector\Rules\UseToBeDirectoryRector
This rule converts is_dir() checks to the toBeDirectory() matcher:
1-expect(is_dir($path))->toBeTrue();2+expect($path)->toBeDirectory();
UseToBeEmptyRector
- class:
Pest\Rector\Rules\UseToBeEmptyRector
This rule converts empty checks and count-zero comparisons to the toBeEmpty() matcher:
1-expect(empty($value))->toBeTrue();2+expect($value)->toBeEmpty();
UseToBeFileRector
- class:
Pest\Rector\Rules\UseToBeFileRector
This rule converts is_file() checks to the toBeFile() matcher:
1-expect(is_file($path))->toBeTrue();2+expect($path)->toBeFile();
UseToBeInRector
- class:
Pest\Rector\Rules\UseToBeInRector
This rule converts strict in_array() checks to the toBeIn() matcher:
1-expect(in_array($value, ['pending', 'active'], true))->toBeTrue();2+expect($value)->toBeIn(['pending', 'active']);
UseToBeInfiniteRector
- class:
Pest\Rector\Rules\UseToBeInfiniteRector
This rule converts is_infinite() checks to the toBeInfinite() matcher:
1-expect(is_infinite($value))->toBeTrue();2+expect($value)->toBeInfinite();
UseToBeJsonRector
- class:
Pest\Rector\Rules\UseToBeJsonRector
This rule converts json_decode() null checks to the toBeJson() matcher:
1-expect(json_decode($string) !== null)->toBeTrue();2+expect($string)->toBeJson();
UseToBeListRector
- class:
Pest\Rector\Rules\UseToBeListRector
This rule converts array_is_list() checks to the toBeList() matcher:
1-expect(array_is_list($array))->toBeTrue();2+expect($array)->toBeList();
UseToBeLowercaseRector
- class:
Pest\Rector\Rules\UseToBeLowercaseRector
This rule converts strtolower() equality checks to the toBeLowercase() matcher:
1-expect(strtolower($value) === $value)->toBeTrue();2+expect($value)->toBeLowercase();
UseToBeNanRector
- class:
Pest\Rector\Rules\UseToBeNanRector
This rule converts is_nan() checks to the toBeNan() matcher:
1-expect(is_nan($value))->toBeTrue();2+expect($value)->toBeNan();
UseToBeUppercaseRector
- class:
Pest\Rector\Rules\UseToBeUppercaseRector
This rule converts strtoupper() equality checks to the toBeUppercase() matcher:
1-expect(strtoupper($value) === $value)->toBeTrue();2+expect($value)->toBeUppercase();
UseToBeUrlRector
- class:
Pest\Rector\Rules\UseToBeUrlRector
This rule converts filter_var($url, FILTER_VALIDATE_URL) checks to the toBeUrl() matcher:
1-expect(filter_var($url, FILTER_VALIDATE_URL))->not->toBeFalse();2+expect($url)->toBeUrl();
UseToBeUuidRector
- class:
Pest\Rector\Rules\UseToBeUuidRector
This rule converts UUID regex validation to the toBeUuid() matcher:
1-expect(preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $value))->toBe(1);2+expect($value)->toBeUuid();
UseToContainEqualRector
- class:
Pest\Rector\Rules\UseToContainEqualRector
This rule converts in_array(..., false) checks to the toContainEqual() matcher:
1-expect(in_array($item, $array, false))->toBeTrue();2+expect($array)->toContainEqual($item);
UseToContainOnlyInstancesOfRector
- class:
Pest\Rector\Rules\UseToContainOnlyInstancesOfRector
This rule converts the ->each->toBeInstanceOf() pattern to the toContainOnlyInstancesOf() matcher:
1-expect($items)->each->toBeInstanceOf(User::class);2+expect($items)->toContainOnlyInstancesOf(User::class);
UseToContainRector
- class:
Pest\Rector\Rules\UseToContainRector
This rule converts in_array() checks to the toContain() matcher:
1-expect(in_array($item, $array))->toBeTrue();2+expect($array)->toContain($item);
UseToEndWithRector
- class:
Pest\Rector\Rules\UseToEndWithRector
This rule converts str_ends_with() checks to the toEndWith() matcher:
1-expect(str_ends_with($string, 'World'))->toBeTrue();2+expect($string)->toEndWith('World');
UseToEqualCanonicalizingRector
- class:
Pest\Rector\Rules\UseToEqualCanonicalizingRector
This rule converts sort-then-compare patterns to the toEqualCanonicalizing() matcher:
1-expect(sort($a))->toEqual(sort($b));2+expect($a)->toEqualCanonicalizing($b);
UseToEqualWithDeltaRector
- class:
Pest\Rector\Rules\UseToEqualWithDeltaRector
This rule converts expect(abs($a - $b) < $delta)->toBeTrue() to expect($a)->toEqualWithDelta($b, $delta):
1-expect(abs($a - $b) < 0.001)->toBeTrue();2+expect($a)->toEqualWithDelta($b, 0.001);
UseToHaveCountRector
- class:
Pest\Rector\Rules\UseToHaveCountRector
This rule converts expect(count($arr))->toBe(5) to expect($arr)->toHaveCount(5):
1-expect(count($array))->toBe(5);2+expect($array)->toHaveCount(5);
UseToHaveKeyRector
- class:
Pest\Rector\Rules\UseToHaveKeyRector
This rule converts array_key_exists() checks to the toHaveKey() matcher:
1-expect(array_key_exists('id', $array))->toBeTrue();2+expect($array)->toHaveKey('id');
UseToHaveKeysRector
- class:
Pest\Rector\Rules\UseToHaveKeysRector
This rule converts chained toHaveKey() calls to toHaveKeys() with an array of keys:
1-expect($array)->toHaveKey('id')->toHaveKey('name');2+expect($array)->toHaveKeys(['id', 'name']);
UseToHaveLengthRector
- class:
Pest\Rector\Rules\UseToHaveLengthRector
This rule converts strlen()/mb_strlen() comparisons to the toHaveLength() matcher:
1-expect(strlen($string))->toBe(10);2+expect($string)->toHaveLength(10);
UseToHavePropertiesRector
- class:
Pest\Rector\Rules\UseToHavePropertiesRector
This rule converts chained toHaveProperty() calls to toHaveProperties() with an array of properties:
1-expect($user)->toHaveProperty('name')->toHaveProperty('email');2+expect($user)->toHaveProperties(['name', 'email']);
UseToHavePropertyRector
- class:
Pest\Rector\Rules\UseToHavePropertyRector
This rule converts property_exists() checks to the toHaveProperty() matcher:
1-expect(property_exists($object, 'name'))->toBeTrue();2+expect($object)->toHaveProperty('name');
UseToHaveSameSizeRector
- class:
Pest\Rector\Rules\UseToHaveSameSizeRector
This rule converts expect(count($a))->toBe(count($b)) to expect($a)->toHaveSameSize($b):
1-expect(count($array1))->toBe(count($array2));2+expect($array1)->toHaveSameSize($array2);
UseToMatchArrayRector
- class:
Pest\Rector\Rules\UseToMatchArrayRector
This rule converts multiple array element assertions to the toMatchArray() matcher:
1-expect($array['name'])->toBe('Nuno');2-expect($array['email'])->toBe('nuno@example.com');3+expect($array)->toMatchArray(['name' => 'Nuno', 'email' => 'nuno@example.com']);
UseToMatchObjectRector
- class:
Pest\Rector\Rules\UseToMatchObjectRector
This rule converts consecutive toHaveProperty() calls with values to the toMatchObject() matcher:
1-expect($user)->toHaveProperty('name', 'Nuno');2-expect($user)->toHaveProperty('email', 'nuno@example.com');3+expect($user)->toMatchObject(['name' => 'Nuno', 'email' => 'nuno@example.com']);
UseToMatchRector
- class:
Pest\Rector\Rules\UseToMatchRector
This rule converts expect(preg_match("/pattern/", $str))->toBe(1) to expect($str)->toMatch("/pattern/"):
1-expect(preg_match('/pattern/', $string))->toBe(1);2+expect($string)->toMatch('/pattern/');
UseToStartWithRector
- class:
Pest\Rector\Rules\UseToStartWithRector
This rule converts str_starts_with() checks to the toStartWith() matcher:
1-expect(str_starts_with($string, 'Hello'))->toBeTrue();2+expect($string)->toStartWith('Hello');
UseToThrowRector
- class:
Pest\Rector\Rules\UseToThrowRector
This rule converts try/catch patterns in Pest tests to expect()->toThrow():
1 test('it throws an error', function () {2- try {3- doSomething();4- } catch (RuntimeException $e) {5- expect($e->getMessage())->toBe('error');6- }7+ expect(fn () => doSomething())->toThrow(RuntimeException::class, 'error');8 });
UseTypeMatchersRector
- class:
Pest\Rector\Rules\UseTypeMatchersRector
This rule converts expect(is_array($x))->toBeTrue() to expect($x)->toBeArray():
1-expect(is_array($value))->toBeTrue();2+expect($value)->toBeArray();
UsesToExtendRector
- class:
Pest\Rector\Rules\Pest2ToPest3\UsesToExtendRector
This rule converts uses() and pest()->uses() to pest()->extend() for classes and pest()->use() for traits:
1-uses(Tests\TestCase::class)->in('Feature');2+pest()->extend(Tests\TestCase::class)->in('Feature');
Now that you know how to automate refactoring your test suite, let's look at how Pest's PHPStan plugin brings accurate static analysis to your tests: PHPStan →