| title | Rector |
|---|---|
| description | Pest's Rector plugin provides automated refactoring rules powered by Rector to modernize your test code and upgrade between Pest versions. |
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.
<iframe width="100%" height="315" src="https://www.youtube.com/embed/oNU41o3pz5M" title="rector rewrote 4000 tests while i got coffee (pest 5, day 5/6)" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>To get started, require the plugin via Composer and install Rector:
composer require pestphp/pest-plugin-rector --dev
composer require rector/rector --devThe plugin provides a predefined rule set: PestSetList::CODING_STYLE. You may register this set in your project's rector.php file.
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. In addition, it includes rules to upgrade your test suite between major Pest versions:
use Pest\Rector\Set\PestSetList;
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([__DIR__ . '/tests'])
->withSets([
PestSetList::CODING_STYLE,
]);To preview changes before applying them, you may run Rector with the --dry-run flag:
vendor/bin/rector process --dry-runOnce you are satisfied with the proposed changes, you may run Rector without the flag to apply them:
vendor/bin/rector processFor reference, below is every rule provided by the plugin, along with an example of the transformation it applies.
- class:
Pest\Rector\Rules\ChainExpectCallsRector
This rule chains multiple expect() calls on the same value into a single chained expectation:
-expect($a)->toBe(10);
-expect($a)->toBeInt();
+expect($a)->toBe(10)
+ ->toBeInt();By default, the rule also joins expectations on different values with ->and():
-expect($a)->toBe(10);
-expect($b)->toBe(10);
+expect($a)->toBe(10)
+ ->and($b)->toBe(10);Sometimes, you may wish to only merge expectations on the same value, leaving expectations on different values untouched. To accomplish this, you may set the merge_different_variables option to false in your project's rector.php file:
use Pest\Rector\Rules\ChainExpectCallsRector;
use Rector\Config\RectorConfig;
return RectorConfig::configure()
->withPaths([__DIR__ . '/tests'])
->withConfiguredRule(ChainExpectCallsRector::class, [
'merge_different_variables' => false,
]);The option defaults to true, so expectations on different values will be joined with ->and() unless you opt out. For example, with merge_different_variables set to false, only expectations on the same value will be merged:
-expect($a)->toBe(10);
-expect($a)->toBeInt();
-expect($b)->toBe(10);
+expect($a)->toBe(10)
+ ->toBeInt();
+expect($b)->toBe(10);- class:
Pest\Rector\Rules\ConvertAssertToExpectRector
This rule converts $this->assert*() calls to Pest expect() chains:
-$this->assertEquals('expected', $result);
-$this->assertTrue($value);
-$this->assertCount(3, $items);
+expect($result)->toEqual('expected');
+expect($value)->toBeTrue();
+expect($items)->toHaveCount(3);- class:
Pest\Rector\Rules\ConvertBeforeAllInDescribeRector
This rule replaces invalid beforeAll() and afterAll() hooks inside describe() with beforeEach() and afterEach():
describe('users', function (): void {
- beforeAll(function (): void {
+ beforeEach(function (): void {
refreshDatabase();
});
});- class:
Pest\Rector\Rules\ConvertExpectExceptionToThrowRector
This rule converts $this->expectException() and $this->expectExceptionMessage() patterns to expect()->toThrow():
-$this->expectException(RuntimeException::class);
-$this->expectExceptionMessage('error');
-doSomething();
+expect(fn () => doSomething())->toThrow(RuntimeException::class, 'error');- 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:
-expect($a)->toBe(10)->toBeInt();
+expect($a)->toBeInt()->toBe(10);- class:
Pest\Rector\Rules\FixInvalidRepeatValueRector
This rule normalizes invalid literal repeat() counts to 1:
it('retries once', function (): void {
expect(true)->toBeTrue();
-})->repeat(0);
+})->repeat(1);- class:
Pest\Rector\Rules\RemoveDebugExpectationsRector
This rule removes debug method calls, such as dump(), dd(), and ray(), from expect() chains:
-expect($user)->dump()->toBeInstanceOf(User::class);
+expect($user)->toBeInstanceOf(User::class);- class:
Pest\Rector\Rules\RemoveOnlyRector
This rule removes only() from all tests:
-test()->only();
+test();- class:
Pest\Rector\Rules\RemoveRedundantLiteralTypeExpectationRector
This rule removes redundant literal type expectations when a later matcher keeps the chain meaningful:
expect('pest')
- ->toBeString()
->toStartWith('p');- class:
Pest\Rector\Rules\RemoveRedundantPestUsesRector
This rule removes redundant local Pest uses() calls that are already configured globally in tests/Pest.php:
// tests/Pest.php contains:
// pest()->use(RefreshDatabase::class)->in('Feature');
// tests/Feature/UserTest.php
-pest()->use(RefreshDatabase::class, SomeOtherTrait::class);
+pest()->use(SomeOtherTrait::class);- class:
Pest\Rector\Rules\RemoveStaticTestClosureRector
This rule removes the static keyword from Pest test and hook callbacks that use the test case instance:
-it('uses the test case instance', static function (): void {
+it('uses the test case instance', function (): void {
expect($this)->not->toBeNull();
});- class:
Pest\Rector\Rules\SimplifyComparisonExpectationsRector
This rule converts expect($x > 10)->toBeTrue() to expect($x)->toBeGreaterThan(10):
-expect($value > 10)->toBeTrue();
-expect($value >= 10)->toBeTrue();
+expect($value)->toBeGreaterThan(10);
+expect($value)->toBeGreaterThanOrEqual(10);- class:
Pest\Rector\Rules\SimplifyExpectNotRector
This rule simplifies negated expectations by flipping the matcher:
-expect(!$condition)->toBeTrue();
+expect($condition)->toBeFalse();- class:
Pest\Rector\Rules\SimplifyFilesystemMatchersRector
This rule simplifies combined filesystem checks to single Pest matchers:
-expect(is_file($path) && is_readable($path))->toBeTrue();
+expect($path)->toBeReadableFile();- class:
Pest\Rector\Rules\SimplifyToBeTruthyFalsyRector
This rule converts boolean cast assertions to the toBeTruthy()/toBeFalsy() matchers:
-expect((bool) $value)->toBeTrue();
+expect($value)->toBeTruthy();- class:
Pest\Rector\Rules\SimplifyToLiteralBooleanRector
This rule simplifies expect($x)->toBe(true) to expect($x)->toBeTrue():
-expect($value)->toBe(true);
-expect($value)->toBe(null);
+expect($value)->toBeTrue();
+expect($value)->toBeNull();- class:
Pest\Rector\Rules\Pest2ToPest3\TapToDeferRector
This rule replaces the deprecated ->tap() method with ->defer() for the Pest v3 migration:
-expect($value)->tap(fn ($value) => dump($value))->toBe(10);
+expect($value)->defer(fn ($value) => dump($value))->toBe(10);- class:
Pest\Rector\Rules\ToBeTrueNotFalseRector
This rule simplifies double-negative expectations like ->not->toBeFalse() to ->toBeTrue():
-expect($value)->not->toBeFalse();
+expect($value)->toBeTrue();- class:
Pest\Rector\Rules\Pest2ToPest3\ToHaveMethodOnClassRector
This rule changes expect($object)->toHaveMethod() to expect($object::class)->toHaveMethod() for Pest v3:
-expect($user)->toHaveMethod('getName');
+expect($user::class)->toHaveMethod('getName');- class:
Pest\Rector\Rules\UseEachModifierRector
This rule converts foreach loops with expect() calls to use the ->each modifier:
-foreach ($items as $item) {
- expect($item)->toBeString();
-}
+expect($items)->each->toBeString();- class:
Pest\Rector\Rules\UseInstanceOfMatcherRector
This rule converts expect($obj instanceof User)->toBeTrue() to expect($obj)->toBeInstanceOf(User::class):
-expect($user instanceof User)->toBeTrue();
+expect($user)->toBeInstanceOf(User::class);- class:
Pest\Rector\Rules\UseSequenceMatcherRector
This rule converts consecutive indexed expect() calls to sequence():
-expect($items[0])->toBe('a');
-expect($items[1])->toBe('b');
+expect($items)->sequence(fn ($e) => $e->toBe('a'), fn ($e) => $e->toBe('b'));- class:
Pest\Rector\Rules\UseStrictEqualityMatchersRector
This rule converts strict equality expressions to the toBe() matcher:
-expect($a === $b)->toBeTrue();
+expect($a)->toBe($b);- class:
Pest\Rector\Rules\UseToBeAlphaNumericRector
This rule converts ctype_alnum() checks to the toBeAlphaNumeric() matcher:
-expect(ctype_alnum($value))->toBeTrue();
+expect($value)->toBeAlphaNumeric();- class:
Pest\Rector\Rules\UseToBeAlphaRector
This rule converts ctype_alpha() checks to the toBeAlpha() matcher:
-expect(ctype_alpha($value))->toBeTrue();
+expect($value)->toBeAlpha();- class:
Pest\Rector\Rules\UseToBeBetweenRector
This rule converts expect($value >= $min && $value <= $max)->toBeTrue() to expect($value)->toBeBetween($min, $max):
-expect($value >= 1 && $value <= 10)->toBeTrue();
+expect($value)->toBeBetween(1, 10);- class:
Pest\Rector\Rules\UseToBeDigitsRector
This rule converts ctype_digit() checks to the toBeDigits() matcher:
-expect(ctype_digit($value))->toBeTrue();
+expect($value)->toBeDigits();- class:
Pest\Rector\Rules\UseToBeDirectoryRector
This rule converts is_dir() checks to the toBeDirectory() matcher:
-expect(is_dir($path))->toBeTrue();
+expect($path)->toBeDirectory();- class:
Pest\Rector\Rules\UseToBeEmptyRector
This rule converts empty checks and count-zero comparisons to the toBeEmpty() matcher:
-expect(empty($value))->toBeTrue();
+expect($value)->toBeEmpty();- class:
Pest\Rector\Rules\UseToBeFileRector
This rule converts is_file() checks to the toBeFile() matcher:
-expect(is_file($path))->toBeTrue();
+expect($path)->toBeFile();- class:
Pest\Rector\Rules\UseToBeInRector
This rule converts strict in_array() checks to the toBeIn() matcher:
-expect(in_array($value, ['pending', 'active'], true))->toBeTrue();
+expect($value)->toBeIn(['pending', 'active']);- class:
Pest\Rector\Rules\UseToBeInfiniteRector
This rule converts is_infinite() checks to the toBeInfinite() matcher:
-expect(is_infinite($value))->toBeTrue();
+expect($value)->toBeInfinite();- class:
Pest\Rector\Rules\UseToBeJsonRector
This rule converts json_decode() null checks to the toBeJson() matcher:
-expect(json_decode($string) !== null)->toBeTrue();
+expect($string)->toBeJson();- class:
Pest\Rector\Rules\UseToBeListRector
This rule converts array_is_list() checks to the toBeList() matcher:
-expect(array_is_list($array))->toBeTrue();
+expect($array)->toBeList();- class:
Pest\Rector\Rules\UseToBeLowercaseRector
This rule converts strtolower() equality checks to the toBeLowercase() matcher:
-expect(strtolower($value) === $value)->toBeTrue();
+expect($value)->toBeLowercase();- class:
Pest\Rector\Rules\UseToBeNanRector
This rule converts is_nan() checks to the toBeNan() matcher:
-expect(is_nan($value))->toBeTrue();
+expect($value)->toBeNan();- class:
Pest\Rector\Rules\UseToBeUppercaseRector
This rule converts strtoupper() equality checks to the toBeUppercase() matcher:
-expect(strtoupper($value) === $value)->toBeTrue();
+expect($value)->toBeUppercase();- class:
Pest\Rector\Rules\UseToBeUrlRector
This rule converts filter_var($url, FILTER_VALIDATE_URL) checks to the toBeUrl() matcher:
-expect(filter_var($url, FILTER_VALIDATE_URL))->not->toBeFalse();
+expect($url)->toBeUrl();- class:
Pest\Rector\Rules\UseToBeUuidRector
This rule converts UUID regex validation to the toBeUuid() matcher:
-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);
+expect($value)->toBeUuid();- class:
Pest\Rector\Rules\UseToContainEqualRector
This rule converts in_array(..., false) checks to the toContainEqual() matcher:
-expect(in_array($item, $array, false))->toBeTrue();
+expect($array)->toContainEqual($item);- class:
Pest\Rector\Rules\UseToContainOnlyInstancesOfRector
This rule converts the ->each->toBeInstanceOf() pattern to the toContainOnlyInstancesOf() matcher:
-expect($items)->each->toBeInstanceOf(User::class);
+expect($items)->toContainOnlyInstancesOf(User::class);- class:
Pest\Rector\Rules\UseToContainRector
This rule converts in_array() checks to the toContain() matcher:
-expect(in_array($item, $array))->toBeTrue();
+expect($array)->toContain($item);- class:
Pest\Rector\Rules\UseToEndWithRector
This rule converts str_ends_with() checks to the toEndWith() matcher:
-expect(str_ends_with($string, 'World'))->toBeTrue();
+expect($string)->toEndWith('World');- class:
Pest\Rector\Rules\UseToEqualCanonicalizingRector
This rule converts sort-then-compare patterns to the toEqualCanonicalizing() matcher:
-expect(sort($a))->toEqual(sort($b));
+expect($a)->toEqualCanonicalizing($b);- class:
Pest\Rector\Rules\UseToEqualWithDeltaRector
This rule converts expect(abs($a - $b) < $delta)->toBeTrue() to expect($a)->toEqualWithDelta($b, $delta):
-expect(abs($a - $b) < 0.001)->toBeTrue();
+expect($a)->toEqualWithDelta($b, 0.001);- class:
Pest\Rector\Rules\UseToHaveCountRector
This rule converts expect(count($arr))->toBe(5) to expect($arr)->toHaveCount(5):
-expect(count($array))->toBe(5);
+expect($array)->toHaveCount(5);- class:
Pest\Rector\Rules\UseToHaveKeyRector
This rule converts array_key_exists() checks to the toHaveKey() matcher:
-expect(array_key_exists('id', $array))->toBeTrue();
+expect($array)->toHaveKey('id');- class:
Pest\Rector\Rules\UseToHaveKeysRector
This rule converts chained toHaveKey() calls to toHaveKeys() with an array of keys:
-expect($array)->toHaveKey('id')->toHaveKey('name');
+expect($array)->toHaveKeys(['id', 'name']);- class:
Pest\Rector\Rules\UseToHaveLengthRector
This rule converts strlen()/mb_strlen() comparisons to the toHaveLength() matcher:
-expect(strlen($string))->toBe(10);
+expect($string)->toHaveLength(10);- class:
Pest\Rector\Rules\UseToHavePropertiesRector
This rule converts chained toHaveProperty() calls to toHaveProperties() with an array of properties:
-expect($user)->toHaveProperty('name')->toHaveProperty('email');
+expect($user)->toHaveProperties(['name', 'email']);- class:
Pest\Rector\Rules\UseToHavePropertyRector
This rule converts property_exists() checks to the toHaveProperty() matcher:
-expect(property_exists($object, 'name'))->toBeTrue();
+expect($object)->toHaveProperty('name');- class:
Pest\Rector\Rules\UseToHaveSameSizeRector
This rule converts expect(count($a))->toBe(count($b)) to expect($a)->toHaveSameSize($b):
-expect(count($array1))->toBe(count($array2));
+expect($array1)->toHaveSameSize($array2);- class:
Pest\Rector\Rules\UseToMatchArrayRector
This rule converts multiple array element assertions to the toMatchArray() matcher:
-expect($array['name'])->toBe('Nuno');
-expect($array['email'])->toBe('nuno@example.com');
+expect($array)->toMatchArray(['name' => 'Nuno', 'email' => 'nuno@example.com']);- class:
Pest\Rector\Rules\UseToMatchObjectRector
This rule converts consecutive toHaveProperty() calls with values to the toMatchObject() matcher:
-expect($user)->toHaveProperty('name', 'Nuno');
-expect($user)->toHaveProperty('email', 'nuno@example.com');
+expect($user)->toMatchObject(['name' => 'Nuno', 'email' => 'nuno@example.com']);- class:
Pest\Rector\Rules\UseToMatchRector
This rule converts expect(preg_match("/pattern/", $str))->toBe(1) to expect($str)->toMatch("/pattern/"):
-expect(preg_match('/pattern/', $string))->toBe(1);
+expect($string)->toMatch('/pattern/');- class:
Pest\Rector\Rules\UseToStartWithRector
This rule converts str_starts_with() checks to the toStartWith() matcher:
-expect(str_starts_with($string, 'Hello'))->toBeTrue();
+expect($string)->toStartWith('Hello');- class:
Pest\Rector\Rules\UseToThrowRector
This rule converts try/catch patterns in Pest tests to expect()->toThrow():
test('it throws an error', function () {
- try {
- doSomething();
- } catch (RuntimeException $e) {
- expect($e->getMessage())->toBe('error');
- }
+ expect(fn () => doSomething())->toThrow(RuntimeException::class, 'error');
});- class:
Pest\Rector\Rules\UseTypeMatchersRector
This rule converts expect(is_array($x))->toBeTrue() to expect($x)->toBeArray():
-expect(is_array($value))->toBeTrue();
+expect($value)->toBeArray();- class:
Pest\Rector\Rules\Pest2ToPest3\UsesToExtendRector
This rule converts uses() and pest()->uses() to pest()->extend() for classes and pest()->use() for traits:
-uses(Tests\TestCase::class)->in('Feature');
+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 →