// docs / rector

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 --dev
2composer 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 v3
5return 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.php
5-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 →