diff --git a/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/remove_add_to_assertion_count.php.inc b/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/remove_add_to_assertion_count.php.inc new file mode 100644 index 00000000..f9b21df4 --- /dev/null +++ b/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/remove_add_to_assertion_count.php.inc @@ -0,0 +1,39 @@ +someMethodCall(); + $this->addToAssertionCount(1); + } + + private function someMethodCall() + { + } +} + +?> +----- +someMethodCall(); + } + + private function someMethodCall() + { + } +} + +?> diff --git a/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/remove_add_to_assertion_count_in_try_catch.php.inc b/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/remove_add_to_assertion_count_in_try_catch.php.inc new file mode 100644 index 00000000..bbd5decd --- /dev/null +++ b/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/remove_add_to_assertion_count_in_try_catch.php.inc @@ -0,0 +1,45 @@ +someMethodCall(); + $this->addToAssertionCount(1); + } catch (\Throwable $throwable) { + } + } + + private function someMethodCall() + { + } +} + +?> +----- +someMethodCall(); + } catch (\Throwable $throwable) { + } + } + + private function someMethodCall() + { + } +} + +?> diff --git a/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/remove_add_to_assertion_count_with_comment.php.inc b/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/remove_add_to_assertion_count_with_comment.php.inc new file mode 100644 index 00000000..28f60efa --- /dev/null +++ b/rules-tests/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector/Fixture/remove_add_to_assertion_count_with_comment.php.inc @@ -0,0 +1,40 @@ +someMethodCall(); + + $this->addToAssertionCount(1); // Verify that no exception is thrown + } + + private function someMethodCall() + { + } +} + +?> +----- +someMethodCall(); + } + + private function someMethodCall() + { + } +} + +?> diff --git a/rules/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector.php b/rules/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector.php index e2314088..7afbafe0 100644 --- a/rules/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector.php +++ b/rules/PHPUnit60/Rector/ClassMethod/AddDoesNotPerformAssertionToNonAssertingTestRector.php @@ -5,7 +5,11 @@ namespace Rector\PHPUnit\PHPUnit60\Rector\ClassMethod; use PhpParser\Node; +use PhpParser\Node\Expr\MethodCall; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Expression; +use PhpParser\Node\Stmt\Nop; +use PhpParser\NodeVisitor; use PHPStan\PhpDocParser\Ast\PhpDoc\GenericTagValueNode; use PHPStan\PhpDocParser\Ast\PhpDoc\PhpDocTagNode; use PHPStan\Reflection\ClassReflection; @@ -104,6 +108,8 @@ public function refactor(Node $node): ?Node return null; } + $this->removeAddToAssertionCountCalls($node); + $phpDocInfo = $this->phpDocInfoFactory->createFromNodeOrEmpty($node); $phpDocInfo->addPhpDocTagNode(new PhpDocTagNode('@doesNotPerformAssertions', new GenericTagValueNode(''))); @@ -143,6 +149,49 @@ private function shouldSkipClassMethod(ClassMethod $classMethod): bool return $this->mockedVariableAnalyzer->containsMockAsUsedVariable($classMethod); } + /** + * The assertion count fakes an assertion, but the "@doesNotPerformAssertions" annotation makes it obsolete + */ + private function removeAddToAssertionCountCalls(ClassMethod $classMethod): void + { + $hasJustRemovedCall = false; + + $this->traverseNodesWithCallable($classMethod, function (Node $node) use (&$hasJustRemovedCall): ?int { + // a comment on the same line as the removed call is parsed as a nop statement right behind it + if ($hasJustRemovedCall && $node instanceof Nop) { + return NodeVisitor::REMOVE_NODE; + } + + $hasJustRemovedCall = false; + + if (! $this->isAddToAssertionCountExpression($node)) { + return null; + } + + $hasJustRemovedCall = true; + + return NodeVisitor::REMOVE_NODE; + }); + } + + private function isAddToAssertionCountExpression(Node $node): bool + { + if (! $node instanceof Expression) { + return false; + } + + if (! $node->expr instanceof MethodCall) { + return false; + } + + $methodCall = $node->expr; + if (! $this->isName($methodCall->var, 'this')) { + return false; + } + + return $this->isName($methodCall->name, 'addToAssertionCount'); + } + private function isInTwigIntegrationTestCase(ClassMethod $classMethod): bool { $classReflection = $this->reflectionResolver->resolveClassReflection($classMethod);