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.
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();
-expect($a)->toBe(10);
-expect($b)->toBe(10);
+expect($a)->toBe(10)
+ ->and($b)->toBe(10);
-expect($a)->toBe(10);
-expect($a)->toBeInt();
-expect($b)->toBe(10);
-expect($b)->toBeInt();
+expect($a)->toBe(10)
+ ->toBeInt()
+ ->and($b)->toBe(10)
+ ->toBeInt();
Converts PHPUnit assertion method calls to Pest expect() chains
-$this->assertEquals('expected', $result);
-$this->assertTrue($value);
-$this->assertCount(3, $items);
-$this->assertNotNull($user);
+expect($result)->toEqual('expected');
+expect($value)->toBeTrue();
+expect($items)->toHaveCount(3);
+expect($user)->not->toBeNull();
-$this->assertIsList($values);
-$this->assertIsNotArray($value);
-$this->assertIsNotBool($value);
-$this->assertIsNotFloat($value);
-$this->assertIsNotInt($value);
-$this->assertIsNotString($value);
-$this->assertIsNotNumeric($value);
-$this->assertIsNotObject($value);
-$this->assertIsNotCallable($value);
-$this->assertIsNotIterable($value);
-$this->assertIsNotScalar($value);
-$this->assertIsNotResource($value);
-$this->assertContainsOnlyInstancesOf(User::class, $users);
-$this->assertSameSize($expected, $actual);
-$this->assertObjectHasProperty('name', $user);
-$this->assertObjectNotHasProperty('password', $user);
-$this->assertEqualsCanonicalizing(['b', 'a'], $letters);
-$this->assertEqualsWithDelta(10.5, $score, 0.1);
-$this->assertContainsEquals(['id' => 1], $items);
-$this->assertNotContainsEquals(['id' => 2], $items);
+expect($values)->toBeList();
+expect($value)->not->toBeArray();
+expect($value)->not->toBeBool();
+expect($value)->not->toBeFloat();
+expect($value)->not->toBeInt();
+expect($value)->not->toBeString();
+expect($value)->not->toBeNumeric();
+expect($value)->not->toBeObject();
+expect($value)->not->toBeCallable();
+expect($value)->not->toBeIterable();
+expect($value)->not->toBeScalar();
+expect($value)->not->toBeResource();
+expect($users)->toContainOnlyInstancesOf(User::class);
+expect($actual)->toHaveSameSize($expected);
+expect($user)->toHaveProperty('name');
+expect($user)->not->toHaveProperty('password');
+expect($letters)->toEqualCanonicalizing(['b', 'a']);
+expect($score)->toEqualWithDelta(10.5, 0.1);
+expect($items)->toContainEqual(['id' => 1]);
+expect($items)->not->toContainEqual(['id' => 2]);
Replaces invalid beforeAll() and afterAll() hooks inside describe() with beforeEach() and afterEach()
describe('users', function (): void {
- beforeAll(function (): void {
+ beforeEach(function (): void {
refreshDatabase();
});
});
Converts expectException() and expectExceptionMessage() patterns to expect()->toThrow()
-$this->expectException(RuntimeException::class);
-$this->expectExceptionMessage('error');
-doSomething();
+expect(fn () => doSomething())->toThrow(RuntimeException::class, 'error');
Ensure type-check matchers (e.g. toBeInt, toBeInstanceOf) appear before value assertions in expect() chains and consecutive expects
-expect($a)->toBe(10)->toBeInt();
+expect($a)->toBeInt()->toBe(10);
-expect($a)->toBe(10);
-expect($a)->toBeInt();
+expect($a)->toBeInt();
+expect($a)->toBe(10);
Normalizes invalid literal repeat() counts to 1
it('retries once', function (): void {
expect(true)->toBeTrue();
-})->repeat(0);
+})->repeat(1);
Removes debug method calls (dump, dd, ray) from expect chains
-expect($user)->dump()->toBeInstanceOf(User::class);
-expect($value)->ray()->toBe(42);
+expect($user)->toBeInstanceOf(User::class);
+expect($value)->toBe(42);
Removes only() from all tests
-test()->only();
+test();
Removes redundant literal type expectations when a later matcher keeps the chain meaningful
expect('pest')
- ->toBeString()
->toStartWith('p');
Removes static 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();
});
Converts expect($x > 10)->toBeTrue() to expect($x)->toBeGreaterThan(10)
-expect($value > 10)->toBeTrue();
-expect($value >= 10)->toBeTrue();
-expect($value < 5)->toBeTrue();
-expect($value <= 5)->toBeTrue();
+expect($value)->toBeGreaterThan(10);
+expect($value)->toBeGreaterThanOrEqual(10);
+expect($value)->toBeLessThan(5);
+expect($value)->toBeLessThanOrEqual(5);
Simplifies negated expectations by flipping the matcher (e.g., expect(!$x)->toBeTrue() becomes expect($x)->toBeFalse())
-expect(!$condition)->toBeTrue();
-expect(!$value)->toBeFalse();
+expect($condition)->toBeFalse();
+expect($value)->toBeTrue();
Simplifies combined filesystem checks to single Pest matchers
-expect(is_file($path) && is_readable($path))->toBeTrue();
-expect($path)->toBeFile()->toBeReadable();
+expect($path)->toBeReadableFile();
+expect($path)->toBeReadableFile();
Converts bool cast assertions to toBeTruthy()/toBeFalsy() matchers
-expect((bool) $value)->toBeTrue();
-expect((bool) $value)->toBeFalse();
+expect($value)->toBeTruthy();
+expect($value)->toBeFalsy();
Simplifies expect($x)->toBe(true) to expect($x)->toBeTrue() and similar patterns
-expect($value)->toBe(true);
-expect($value)->toBe(false);
-expect($value)->toBe(null);
-expect($value)->toEqual([]);
-expect($value)->toBe('');
+expect($value)->toBeTrue();
+expect($value)->toBeFalse();
+expect($value)->toBeNull();
+expect($value)->toBeEmpty();
+expect($value)->toBeEmpty();
Replaces deprecated ->tap() method with ->defer() for Pest v3 migration
-expect($value)->tap(fn ($value) => dump($value))->toBe(10);
+expect($value)->defer(fn ($value) => dump($value))->toBe(10);
Simplifies double-negative expectations like ->not->toBeFalse() to ->toBeTrue()
-expect($value)->not->toBeFalse();
-expect($value)->not->toBeTrue();
+expect($value)->toBeTrue();
+expect($value)->toBeFalse();
Changes expect($object)->toHaveMethod() to expect($object::class)->toHaveMethod() for Pest v3
-expect($user)->toHaveMethod('getName');
-expect($user)->toHaveMethods(['getName', 'getEmail']);
+expect($user::class)->toHaveMethod('getName');
+expect($user::class)->toHaveMethods(['getName', 'getEmail']);
Converts expect($page->attribute($selector, "aria-"))->toBe($value) to $page->assertAriaAttribute($selector, $attr, $value) and the data- equivalent
-expect($page->attribute('button', 'aria-label'))->toBe('Close');
-expect($page->attribute('div', 'data-id'))->toBe('123');
+$page->assertAriaAttribute('button', 'label', 'Close');
+$page->assertDataAttribute('div', 'id', '123');
Converts expect($page->attribute($selector, $attr))->toBe($value) to $page->assertAttribute($selector, $attr, $value)
-expect($page->attribute('img', 'alt'))->toBe('Profile Picture');
-expect($page->attribute('div', 'class'))->toContain('container');
-expect($page->attribute('div', 'class'))->not->toContain('hidden');
-expect($page->attribute('button', 'disabled'))->toBeNull();
+$page->assertAttribute('img', 'alt', 'Profile Picture');
+$page->assertAttributeContains('div', 'class', 'container');
+$page->assertAttributeDoesntContain('div', 'class', 'hidden');
+$page->assertAttributeMissing('button', 'disabled');
Converts expect($page->script($expression))->toBe($value) to $page->assertScript($expression, $value)
-expect($page->script('document.title'))->toBe('Home Page');
-expect($page->script('document.querySelector(".btn").disabled'))->toBe(true);
-expect($page->script('1 + 1'))->toEqual(2);
+$page->assertScript('document.title', 'Home Page');
+$page->assertScript('document.querySelector(".btn").disabled', true);
+$page->assertScript('1 + 1', 2);
Converts expect($page->content())->toContain($html) to $page->assertSourceHas($html)
-expect($page->content())->toContain('<h1>Welcome</h1>');
-expect($page->content())->not->toContain('<div class="error">');
+$page->assertSourceHas('<h1>Welcome</h1>');
+$page->assertSourceMissing('<div class="error">');
Converts expect($page->url())->toBe($url) to $page->assertUrlIs($url)
-expect($page->url())->toBe('https://example.com/home');
+$page->assertUrlIs('https://example.com/home');
Converts expect($page->value($selector))->toBe($value) to $page->assertValue($selector, $value)
-expect($page->value('input[name=email]'))->toBe('test@example.com');
-expect($page->value('input[name=email]'))->not->toBe('wrong@example.com');
+$page->assertValue('input[name=email]', 'test@example.com');
+$page->assertValueIsNot('input[name=email]', 'wrong@example.com');
Converts foreach loops with expect() calls to use the ->each modifier
-foreach ($items as $item) {
- expect($item)->toBeString();
-}
+expect($items)->each->toBeString();
Converts expect($obj instanceof User)->toBeTrue() to expect($obj)->toBeInstanceOf(User::class)
-expect($user instanceof User)->toBeTrue();
-expect($object instanceof DateTime)->toBeTrue();
+expect($user)->toBeInstanceOf(User::class);
+expect($object)->toBeInstanceOf(DateTime::class);
Converts consecutive indexed expect() calls to sequence()
-expect($items[0])->toBe('a');
-expect($items[1])->toBe('b');
-expect($items[2])->toBe('c');
+expect($items)->sequence(fn ($e) => $e->toBe('a'), fn ($e) => $e->toBe('b'), fn ($e) => $e->toBe('c'));
Converts strict equality expressions to toBe() matcher
-expect($a === $b)->toBeTrue();
-expect($value === 'expected')->toBeTrue();
-expect($a !== $b)->toBeTrue();
+expect($a)->toBe($b);
+expect($value)->toBe('expected');
+expect($a)->not->toBe($b);
Converts ctype_alnum() checks to toBeAlphaNumeric() matcher
-expect(ctype_alnum($value))->toBeTrue();
+expect($value)->toBeAlphaNumeric();
Converts ctype_alpha() checks to toBeAlpha() matcher
-expect(ctype_alpha($value))->toBeTrue();
+expect($value)->toBeAlpha();
Converts expect($value >= $min && $value <= $max)->toBeTrue() to expect($value)->toBeBetween($min, $max)
-expect($value >= 1 && $value <= 10)->toBeTrue();
-expect($age >= 18 && $age <= 65)->toBeTrue();
+expect($value)->toBeBetween(1, 10);
+expect($age)->toBeBetween(18, 65);
Converts Str::camel() equality checks to toBeCamelCase() matcher (requires illuminate/support)
-expect(Str::camel($value) === $value)->toBeTrue();
+expect($value)->toBeCamelCase();
Converts ctype_digit() checks to toBeDigits() matcher
-expect(ctype_digit($value))->toBeTrue();
+expect($value)->toBeDigits();
Converts is_dir() checks to toBeDirectory() matcher
-expect(is_dir($path))->toBeTrue();
-expect(is_dir('/tmp'))->toBeTrue();
+expect($path)->toBeDirectory();
+expect('/tmp')->toBeDirectory();
Converts empty checks and count-zero comparisons to toBeEmpty() matcher
-expect(empty($value))->toBeTrue();
-expect(count($array))->toBe(0);
-expect($array)->toHaveCount(0);
+expect($value)->toBeEmpty();
+expect($array)->toBeEmpty();
+expect($array)->toBeEmpty();
Converts is_file() checks to toBeFile() matcher
-expect(is_file($path))->toBeTrue();
-expect(is_file('/tmp/file.txt'))->toBeTrue();
+expect($path)->toBeFile();
+expect('/tmp/file.txt')->toBeFile();
Converts in_array() with value first to toBeIn() matcher
-expect(in_array($value, ['pending', 'active']))->toBeTrue();
-expect(in_array($status, $allowedStatuses))->toBeTrue();
+expect($value)->toBeIn(['pending', 'active']);
+expect($status)->toBeIn($allowedStatuses);
Converts is_infinite() checks to toBeInfinite() matcher
-expect(is_infinite($value))->toBeTrue();
+expect($value)->toBeInfinite();
Converts json_decode() null checks to toBeJson() matcher
-expect(json_decode($string) !== null)->toBeTrue();
-expect(json_decode($json) === null)->toBeFalse();
+expect($string)->toBeJson();
+expect($json)->toBeJson();
Converts Str::kebab() equality checks to toBeKebabCase() matcher (requires illuminate/support)
-expect(Str::kebab($value) === $value)->toBeTrue();
+expect($value)->toBeKebabCase();
Converts array_is_list() checks to toBeList() matcher
-expect(array_is_list($array))->toBeTrue();
+expect($array)->toBeList();
Converts strtolower() equality checks to toBeLowercase() matcher
-expect(strtolower($value) === $value)->toBeTrue();
-expect($value === strtolower($value))->toBeTrue();
+expect($value)->toBeLowercase();
+expect($value)->toBeLowercase();
Converts is_nan() checks to toBeNan() matcher
-expect(is_nan($value))->toBeTrue();
+expect($value)->toBeNan();
Converts is_readable()/is_writable() checks to toBeReadable()/toBeWritable() matchers
-expect(is_readable($path))->toBeTrue();
-expect(is_writable($file))->toBeTrue();
+expect($path)->toBeReadable();
+expect($file)->toBeWritable();
Converts Str::slug() equality checks to toBeSlug() matcher (requires illuminate/support)
-expect(Str::slug($value) === $value)->toBeTrue();
+expect($value)->toBeSlug();
Converts Str::snake() equality checks to toBeSnakeCase() matcher (requires illuminate/support)
-expect(Str::snake($value) === $value)->toBeTrue();
+expect($value)->toBeSnakeCase();
Converts Str::studly() equality checks to toBeStudlyCase() matcher (requires illuminate/support)
-expect(Str::studly($value) === $value)->toBeTrue();
+expect($value)->toBeStudlyCase();
Converts strtoupper() equality checks to toBeUppercase() matcher
-expect(strtoupper($value) === $value)->toBeTrue();
-expect($value === strtoupper($value))->toBeTrue();
+expect($value)->toBeUppercase();
+expect($value)->toBeUppercase();
Converts filter_var($url, FILTER_VALIDATE_URL) checks to toBeUrl() matcher
-expect(filter_var($url, FILTER_VALIDATE_URL))->not->toBeFalse();
-expect(filter_var($url, FILTER_VALIDATE_URL) !== false)->toBeTrue();
+expect($url)->toBeUrl();
+expect($url)->toBeUrl();
Converts UUID regex validation to 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(preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i', $uuid))->toBeGreaterThan(0);
+expect($value)->toBeUuid();
+expect($uuid)->toBeUuid();
Converts in_array(..., false) checks to toContainEqual() matcher
-expect(in_array($...
How can I help you explore Laravel packages today?