diff --git a/config/sets/phpunit-code-quality.php b/config/sets/phpunit-code-quality.php index ccc90e79..9ac2ba9f 100644 --- a/config/sets/phpunit-code-quality.php +++ b/config/sets/phpunit-code-quality.php @@ -31,6 +31,7 @@ use Rector\PHPUnit\CodeQuality\Rector\MethodCall\AssertSameTrueFalseToAssertTrueFalseRector; use Rector\PHPUnit\CodeQuality\Rector\MethodCall\AssertTrueFalseToSpecificMethodRector; use Rector\PHPUnit\CodeQuality\Rector\MethodCall\FlipAssertRector; +use Rector\PHPUnit\CodeQuality\Rector\MethodCall\MatchAssertSameExpectedTypeRector; use Rector\PHPUnit\CodeQuality\Rector\MethodCall\NarrowIdenticalWithConsecutiveRector; use Rector\PHPUnit\CodeQuality\Rector\MethodCall\NarrowSingleWillReturnCallbackRector; use Rector\PHPUnit\CodeQuality\Rector\MethodCall\RemoveExpectAnyFromMockRector; @@ -45,6 +46,7 @@ ConstructClassMethodToSetUpTestCaseRector::class, AssertSameTrueFalseToAssertTrueFalseRector::class, + MatchAssertSameExpectedTypeRector::class, AssertEqualsToSameRector::class, PreferPHPUnitThisCallRector::class, diff --git a/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/match_assert_type.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/match_assert_type.php.inc new file mode 100644 index 00000000..1c537c53 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/match_assert_type.php.inc @@ -0,0 +1,41 @@ +assertSame('123', $this->getOrderId()); + } + + private function getOrderId(): int + { + return 123; + } +} + +?> +----- +assertSame(123, $this->getOrderId()); + } + + private function getOrderId(): int + { + return 123; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/match_integer_to_string.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/match_integer_to_string.php.inc new file mode 100644 index 00000000..4f0d419e --- /dev/null +++ b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/match_integer_to_string.php.inc @@ -0,0 +1,41 @@ +assertSame(123, $this->getOrderId()); + } + + private function getOrderId(): string + { + return '123'; + } +} + +?> +----- +assertSame('123', $this->getOrderId()); + } + + private function getOrderId(): string + { + return '123'; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/nullable_match_assert_type.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/nullable_match_assert_type.php.inc new file mode 100644 index 00000000..db905973 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/nullable_match_assert_type.php.inc @@ -0,0 +1,41 @@ +assertSame('123', $this->getOrderId()); + } + + private function getOrderId(): ?int + { + return 123; + } +} + +?> +----- +assertSame(123, $this->getOrderId()); + } + + private function getOrderId(): ?int + { + return 123; + } +} + +?> diff --git a/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/skip_argument_less.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/skip_argument_less.php.inc new file mode 100644 index 00000000..1bbaceb1 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/skip_argument_less.php.inc @@ -0,0 +1,13 @@ +assertSame(1); + } +} diff --git a/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/skip_docblock_type.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/skip_docblock_type.php.inc new file mode 100644 index 00000000..a567ba36 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/skip_docblock_type.php.inc @@ -0,0 +1,21 @@ +assertSame('123', $this->getOrderId()); + } + + /** + * @return int + */ + private function getOrderId() + { + return '123'; + } +} diff --git a/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/skip_first_class_callable.php.inc b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/skip_first_class_callable.php.inc new file mode 100644 index 00000000..879c370f --- /dev/null +++ b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/Fixture/skip_first_class_callable.php.inc @@ -0,0 +1,13 @@ +assertSame(...); + } +} diff --git a/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/MatchAssertSameExpectedTypeRectorTest.php b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/MatchAssertSameExpectedTypeRectorTest.php new file mode 100644 index 00000000..fdd13a92 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/MatchAssertSameExpectedTypeRectorTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/config/configured_rule.php b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/config/configured_rule.php new file mode 100644 index 00000000..bc288d66 --- /dev/null +++ b/rules-tests/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector/config/configured_rule.php @@ -0,0 +1,10 @@ +rule(MatchAssertSameExpectedTypeRector::class); +}; diff --git a/rules/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector.php b/rules/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector.php new file mode 100644 index 00000000..575ce818 --- /dev/null +++ b/rules/CodeQuality/Rector/MethodCall/MatchAssertSameExpectedTypeRector.php @@ -0,0 +1,126 @@ +assertSame('123', $this->getOrderId()); + } + + private function getOrderId(): int + { + return 123; + } +} +CODE_SAMPLE + , + <<<'CODE_SAMPLE' +use PHPUnit\Framework\TestCase; + +class SomeTest extends TestCase +{ + public function run() + { + $this->assertSame(123, $this->getOrderId()); + } + + private function getOrderId(): int + { + return 123; + } +} +CODE_SAMPLE + ), + ] + ); + } + + /** + * @return array> + */ + public function getNodeTypes(): array + { + return [MethodCall::class, StaticCall::class]; + } + + /** + * @param MethodCall|StaticCall $node + */ + public function refactor(Node $node): ?Node + { + if (! $this->testsNodeAnalyzer->isPHPUnitMethodCallNames($node, ['assertSame', 'assertEquals'])) { + return null; + } + + if ($node->isFirstClassCallable()) { + return null; + } + + if (count($node->getArgs()) < 2) { + return null; + } + + $expectedArg = $node->getArgs()[0]; + if (! $expectedArg->value instanceof String_ && ! $expectedArg->value instanceof Int_) { + return null; + } + + $expectedType = $this->getType($expectedArg->value); + + $variableExpr = $node->getArgs()[1] + ->value; + $variableType = $this->nodeTypeResolver->getNativeType($variableExpr); + + $directVariableType = TypeCombinator::removeNull($variableType); + + if ($expectedType->isLiteralString()->yes() && $directVariableType->isInteger()->yes()) { + // update expected type to provided type + $expectedArg->value = new Int_((int) $expectedArg->value->value); + + return $node; + } + + if ($expectedType->isInteger()->yes() && $directVariableType->isString()->yes()) { + // update expected type to provided type + $expectedArg->value = new String_((string) $expectedArg->value->value); + + return $node; + } + + return null; + } +} diff --git a/tests/Issues/AssertEqualsSame/AssertEqualsSameTest.php b/tests/Issues/AssertEqualsSame/AssertEqualsSameTest.php new file mode 100644 index 00000000..b0315b6f --- /dev/null +++ b/tests/Issues/AssertEqualsSame/AssertEqualsSameTest.php @@ -0,0 +1,28 @@ +doTestFile($filePath); + } + + public static function provideData(): Iterator + { + return self::yieldFilesFromDirectory(__DIR__ . '/Fixture'); + } + + public function provideConfigFilePath(): string + { + return __DIR__ . '/config/configured_rule.php'; + } +} diff --git a/tests/Issues/AssertEqualsSame/Fixture/skip_assert_equals.php.inc b/tests/Issues/AssertEqualsSame/Fixture/skip_assert_equals.php.inc new file mode 100644 index 00000000..bda8d757 --- /dev/null +++ b/tests/Issues/AssertEqualsSame/Fixture/skip_assert_equals.php.inc @@ -0,0 +1,20 @@ +assertEquals(123, $this->getOrderId()); + } + + private function getOrderId(): string + { + return '0000000000000000000123'; + } +} diff --git a/tests/Issues/AssertEqualsSame/config/configured_rule.php b/tests/Issues/AssertEqualsSame/config/configured_rule.php new file mode 100644 index 00000000..c47b46fb --- /dev/null +++ b/tests/Issues/AssertEqualsSame/config/configured_rule.php @@ -0,0 +1,11 @@ +rules([MatchAssertSameExpectedTypeRector::class, AssertEqualsToSameRector::class]); +};