Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions config/sets/phpunit-code-quality.php
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
use Rector\PHPUnit\CodeQuality\Rector\MethodCall\WithCallbackIdenticalToStandaloneAssertsRector;
use Rector\PHPUnit\CodeQuality\Rector\StmtsAwareInterface\DeclareStrictTypesTestsRector;
use Rector\PHPUnit\PHPUnit120\Rector\CallLike\CreateStubOverCreateMockArgRector;
use Rector\PHPUnit\PHPUnit120\Rector\Class_\PropertyCreateMockToCreateStubRector;
use Rector\PHPUnit\PHPUnit120\Rector\ClassMethod\ExpressionCreateMockToCreateStubRector;
use Rector\PHPUnit\PHPUnit60\Rector\MethodCall\GetMockBuilderGetMockToCreateMockRector;
use Rector\PHPUnit\PHPUnit90\Rector\MethodCall\ReplaceAtMethodWithDesiredMatcherRector;
Expand Down Expand Up @@ -130,6 +131,7 @@
// stub over mock
CreateStubOverCreateMockArgRector::class,
ExpressionCreateMockToCreateStubRector::class,
PropertyCreateMockToCreateStubRector::class,

// @test first, enable later
// \Rector\PHPUnit\CodeQuality\Rector\Expression\ConfiguredMockEntityToSetterObjectRector::class,
Expand Down
2 changes: 2 additions & 0 deletions config/sets/phpunit120.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Rector\Config\RectorConfig;
use Rector\PHPUnit\PHPUnit120\Rector\CallLike\CreateStubOverCreateMockArgRector;
use Rector\PHPUnit\PHPUnit120\Rector\Class_\AssertIsTypeMethodCallRector;
use Rector\PHPUnit\PHPUnit120\Rector\Class_\PropertyCreateMockToCreateStubRector;
use Rector\PHPUnit\PHPUnit120\Rector\Class_\RemoveOverrideFinalConstructTestCaseRector;
use Rector\PHPUnit\PHPUnit120\Rector\ClassMethod\ExpressionCreateMockToCreateStubRector;

Expand All @@ -16,6 +17,7 @@
// stubs over mocks
CreateStubOverCreateMockArgRector::class,
ExpressionCreateMockToCreateStubRector::class,
PropertyCreateMockToCreateStubRector::class,

// experimental, from PHPUnit 12.5.2
// @see https://github.com/sebastianbergmann/phpunit/commit/24c208d6a340c3071f28a9b5cce02b9377adfd43
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
<?php

namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\Class_\PropertyCreateMockToCreateStubRector\Fixture;

use PHPUnit\Framework\TestCase;

final class SomeTest extends TestCase
{
private \PHPUnit\Framework\MockObject\MockObject $someMock;

protected function setUp(): void
{
$this->someMock = $this->createMock(\stdClass::class);
}

public function testThis()
{
$this->assertSame('...', $this->someMock);
}

public function testThat()
{
$this->assertSame('...', $this->someMock);
}
}

?>
-----
<?php

namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\Class_\PropertyCreateMockToCreateStubRector\Fixture;

use PHPUnit\Framework\TestCase;

final class SomeTest extends TestCase
{
private \PHPUnit\Framework\MockObject\Stub $someMock;

protected function setUp(): void
{
$this->someMock = $this->createStub(\stdClass::class);
}

public function testThis()
{
$this->assertSame('...', $this->someMock);
}

public function testThat()
{
$this->assertSame('...', $this->someMock);
}
}

?>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\Class_\PropertyCreateMockToCreateStubRector\Fixture;

use PHPUnit\Framework\TestCase;

final class SkipPropertyUse extends TestCase
{
private \PHPUnit\Framework\MockObject\MockObject $someMock;

protected function setUp(): void
{
$this->someMock = $this->createMock(\stdClass::class);
}

public function testThis()
{
$this->someMock->expects($this->atLeastOnce())->method('something');
$this->assertSame('...', $this->someMock);
}

public function testThat()
{
$this->assertSame('...', $this->someMock);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

declare(strict_types=1);

namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\Class_\PropertyCreateMockToCreateStubRector;

use Iterator;
use PHPUnit\Framework\Attributes\DataProvider;
use Rector\Testing\PHPUnit\AbstractRectorTestCase;

final class PropertyCreateMockToCreateStubRectorTest extends AbstractRectorTestCase
{
#[DataProvider('provideData')]
public function test(string $filePath): void
{
$this->doTestFile($filePath);
}

public static function provideData(): Iterator
{
return self::yieldFilesFromDirectory(__DIR__ . '/Fixture');
}

public function provideConfigFilePath(): string
{
return __DIR__ . '/config/configured_rule.php';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Rector\PHPUnit\Tests\PHPUnit120\Rector\Class_\PropertyCreateMockToCreateStubRector\Source;

final class ClassWithDependency
{
public function __construct(
private $dependency,
) {
}

public function getDependency()
{
return $this->dependency;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php

declare(strict_types=1);

use Rector\Config\RectorConfig;
use Rector\PHPUnit\PHPUnit120\Rector\Class_\PropertyCreateMockToCreateStubRector;

return RectorConfig::configure()
->withRules(rules: [PropertyCreateMockToCreateStubRector::class]);
23 changes: 23 additions & 0 deletions rules/CodeQuality/NodeAnalyser/MockObjectExprDetector.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@

use PhpParser\Node\Expr;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Expr\Variable;
use PhpParser\Node\Stmt\Class_;
use PhpParser\Node\Stmt\ClassMethod;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\PhpParser\Node\BetterNodeFinder;
Expand Down Expand Up @@ -58,4 +60,25 @@ public function isUsedForMocking(Expr $expr, ClassMethod $classMethod): bool

return false;
}

public function isPropertyUsedForMocking(Class_ $class, string $propertyName): bool
{
// find out, how many are used in call likes as args
/** @var array<Expr\MethodCall> $methodCalls */
$methodCalls = $this->betterNodeFinder->findInstancesOfScoped($class->getMethods(), [MethodCall::class]);

foreach ($methodCalls as $methodCall) {
if (! $methodCall->var instanceof PropertyFetch) {
continue;
}

$propertyFetch = $methodCall->var;
if ($this->nodeNameResolver->isName($propertyFetch->name, $propertyName)) {
// variable is being called on, most like mocking, lets skip
return true;
}
}

return false;
}
}
57 changes: 56 additions & 1 deletion rules/CodeQuality/NodeAnalyser/MockObjectPropertyDetector.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,23 @@

namespace Rector\PHPUnit\CodeQuality\NodeAnalyser;

use PhpParser\Node\Expr\Assign;
use PhpParser\Node\Expr\MethodCall;
use PhpParser\Node\Expr\PropertyFetch;
use PhpParser\Node\Name\FullyQualified;
use PhpParser\Node\Stmt\ClassMethod;
use PhpParser\Node\Stmt\Expression;
use PhpParser\Node\Stmt\Property;
use Rector\NodeNameResolver\NodeNameResolver;
use Rector\PHPUnit\Enum\PHPUnitClassName;

final class MockObjectPropertyDetector
final readonly class MockObjectPropertyDetector
{
public function __construct(
private NodeNameResolver $nodeNameResolver
) {
}

public function detect(Property $property): bool
{
if (! $property->type instanceof FullyQualified) {
Expand All @@ -18,4 +29,48 @@ public function detect(Property $property): bool

return $property->type->toString() === PHPUnitClassName::MOCK_OBJECT;
}

/**
* @return array<string, MethodCall>
*/
public function collectFromClassMethod(ClassMethod $classMethod): array
{
$propertyNamesToCreateMockMethodCalls = [];

foreach ((array) $classMethod->stmts as $stmt) {
if (! $stmt instanceof Expression) {
continue;
}

if (! $stmt->expr instanceof Assign) {
continue;
}

$assign = $stmt->expr;

if (! $assign->var instanceof PropertyFetch) {
continue;
}

if (! $assign->expr instanceof MethodCall) {
continue;
}

$methodCall = $assign->expr;
if (! $this->nodeNameResolver->isName($methodCall->name, 'createMock')) {
continue;
}

$propertyFetch = $assign->var;
$propertyName = $this->nodeNameResolver->getName($propertyFetch->name);

if (! is_string($propertyName)) {
continue;
}

$propertyNamesToCreateMockMethodCalls[$propertyName] = $methodCall;
}

return $propertyNamesToCreateMockMethodCalls;
}
}
Loading