-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Report error when trying to configure a non existing method on MockOb…
…ject
- Loading branch information
1 parent
1648b3d
commit 1d3cfe3
Showing
8 changed files
with
264 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Rules\PHPUnit; | ||
|
||
use PhpParser\Node; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Type\Constant\ConstantStringType; | ||
use PHPStan\Type\Generic\GenericObjectType; | ||
use PHPStan\Type\IntersectionType; | ||
use PHPStan\Type\ObjectType; | ||
use PHPUnit\Framework\MockObject\InvocationMocker; | ||
use PHPUnit\Framework\MockObject\MockObject; | ||
|
||
/** | ||
* @implements \PHPStan\Rules\Rule<\PhpParser\Node\Expr\MethodCall> | ||
*/ | ||
class MockMethodCallRule implements \PHPStan\Rules\Rule | ||
{ | ||
|
||
public function getNodeType(): string | ||
{ | ||
return Node\Expr\MethodCall::class; | ||
} | ||
|
||
public function processNode(Node $node, Scope $scope): array | ||
{ | ||
/** @var Node\Expr\MethodCall $node */ | ||
$node = $node; | ||
|
||
if (!$node->name instanceof Node\Identifier || $node->name->name !== 'method') { | ||
return []; | ||
} | ||
|
||
if (count($node->args) < 1) { | ||
return []; | ||
} | ||
|
||
$argType = $scope->getType($node->args[0]->value); | ||
if (!($argType instanceof ConstantStringType)) { | ||
return []; | ||
} | ||
|
||
$method = $argType->getValue(); | ||
$type = $scope->getType($node->var); | ||
|
||
if ( | ||
$type instanceof IntersectionType | ||
&& in_array(MockObject::class, $type->getReferencedClasses(), true) | ||
&& !$type->hasMethod($method)->yes() | ||
) { | ||
$mockClass = array_filter($type->getReferencedClasses(), function (string $class): bool { | ||
return $class !== MockObject::class; | ||
}); | ||
|
||
return [ | ||
sprintf( | ||
'Trying to mock an undefined method %s() on class %s.', | ||
$method, | ||
\implode('&', $mockClass) | ||
), | ||
]; | ||
} | ||
|
||
if ( | ||
$type instanceof GenericObjectType | ||
&& $type->getClassName() === InvocationMocker::class | ||
&& count($type->getTypes()) > 0 | ||
) { | ||
$mockClass = $type->getTypes()[0]; | ||
|
||
if ($mockClass instanceof ObjectType && !$mockClass->hasMethod($method)->yes()) { | ||
return [ | ||
sprintf( | ||
'Trying to mock an undefined method %s() on class %s.', | ||
$method, | ||
$mockClass->getClassName() | ||
), | ||
]; | ||
} | ||
} | ||
|
||
return []; | ||
} | ||
|
||
} |
29 changes: 29 additions & 0 deletions
29
src/Type/PHPUnit/InvocationMockerDynamicReturnTypeExtension.php
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Type\PHPUnit; | ||
|
||
use PhpParser\Node\Expr\MethodCall; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Reflection\MethodReflection; | ||
use PHPStan\Type\Type; | ||
use PHPUnit\Framework\MockObject\Builder\InvocationMocker; | ||
|
||
class InvocationMockerDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension | ||
{ | ||
|
||
public function getClass(): string | ||
{ | ||
return InvocationMocker::class; | ||
} | ||
|
||
public function isMethodSupported(MethodReflection $methodReflection): bool | ||
{ | ||
return $methodReflection->getName() !== 'getMatcher'; | ||
} | ||
|
||
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type | ||
{ | ||
return $scope->getType($methodCall->var); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Type\PHPUnit; | ||
|
||
use PhpParser\Node\Expr\MethodCall; | ||
use PHPStan\Analyser\Scope; | ||
use PHPStan\Reflection\MethodReflection; | ||
use PHPStan\Type\Generic\GenericObjectType; | ||
use PHPStan\Type\IntersectionType; | ||
use PHPStan\Type\Type; | ||
use PHPStan\Type\TypeWithClassName; | ||
use PHPUnit\Framework\MockObject\InvocationMocker; | ||
use PHPUnit\Framework\MockObject\MockObject; | ||
|
||
class MockObjectDynamicReturnTypeExtension implements \PHPStan\Type\DynamicMethodReturnTypeExtension | ||
{ | ||
|
||
public function getClass(): string | ||
{ | ||
return MockObject::class; | ||
} | ||
|
||
public function isMethodSupported(MethodReflection $methodReflection): bool | ||
{ | ||
return $methodReflection->getName() === 'expects'; | ||
} | ||
|
||
public function getTypeFromMethodCall(MethodReflection $methodReflection, MethodCall $methodCall, Scope $scope): Type | ||
{ | ||
$type = $scope->getType($methodCall->var); | ||
if (!($type instanceof IntersectionType)) { | ||
return new GenericObjectType(InvocationMocker::class, []); | ||
} | ||
|
||
$mockClasses = array_filter($type->getTypes(), function (Type $type): bool { | ||
return !$type instanceof TypeWithClassName || $type->getClassName() !== MockObject::class; | ||
}); | ||
|
||
return new GenericObjectType(InvocationMocker::class, $mockClasses); | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
<?php | ||
|
||
namespace PHPUnit\Framework\MockObject\Builder; | ||
|
||
use PHPUnit\Framework\MockObject\Stub; | ||
|
||
/** | ||
* @template TMockedClass | ||
*/ | ||
class InvocationMocker | ||
{ | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace PHPStan\Rules\PHPUnit; | ||
|
||
use PHPStan\Rules\Rule; | ||
|
||
/** | ||
* @extends \PHPStan\Testing\RuleTestCase<MockMethodCallRule> | ||
*/ | ||
class MockMethodCallRuleTest extends \PHPStan\Testing\RuleTestCase | ||
{ | ||
|
||
protected function getRule(): Rule | ||
{ | ||
return new MockMethodCallRule(); | ||
} | ||
|
||
public function testRule(): void | ||
{ | ||
$this->analyse([__DIR__ . '/data/mock-method-call.php'], [ | ||
[ | ||
'Trying to mock an undefined method doBadThing() on class MockMethodCall\Bar.', | ||
15, | ||
], | ||
[ | ||
'Trying to mock an undefined method doBadThing() on class MockMethodCall\Bar.', | ||
20, | ||
], | ||
]); | ||
} | ||
|
||
/** | ||
* @return string[] | ||
*/ | ||
public static function getAdditionalConfigFiles(): array | ||
{ | ||
return [ | ||
__DIR__ . '/../../../extension.neon', | ||
]; | ||
} | ||
|
||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
<?php declare(strict_types = 1); | ||
|
||
namespace MockMethodCall; | ||
|
||
class Foo extends \PHPUnit\Framework\TestCase | ||
{ | ||
|
||
public function testGoodMethod() | ||
{ | ||
$this->createMock(Bar::class)->method('doThing'); | ||
} | ||
|
||
public function testBadMethod() | ||
{ | ||
$this->createMock(Bar::class)->method('doBadThing'); | ||
} | ||
|
||
public function testBadMethodWithExpectation() | ||
{ | ||
$this->createMock(Bar::class)->expects($this->once())->method('doBadThing'); | ||
} | ||
|
||
public function testWithAnotherObject() | ||
{ | ||
$bar = new BarWithMethod(); | ||
$bar->method('doBadThing'); | ||
} | ||
|
||
} | ||
|
||
class Bar { | ||
public function doThing() | ||
{ | ||
return 1; | ||
} | ||
}; | ||
|
||
class BarWithMethod { | ||
public function method(string $string) | ||
{ | ||
return $string; | ||
} | ||
}; |