Skip to content

Commit 1feded4

Browse files
Report error when trying to configure a non existing method on MockObject
1 parent 7232c17 commit 1feded4

File tree

3 files changed

+140
-0
lines changed

3 files changed

+140
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\PHPUnit;
4+
5+
use PhpParser\Node;
6+
use PHPStan\Analyser\Scope;
7+
use PHPStan\Type\IntersectionType;
8+
use PHPStan\Type\ObjectType;
9+
use PHPUnit\Framework\MockObject\MockObject;
10+
11+
/**
12+
* @implements \PHPStan\Rules\Rule<\PhpParser\Node\Expr\MethodCall>
13+
*/
14+
class MockMethodCallRule implements \PHPStan\Rules\Rule
15+
{
16+
17+
public function getNodeType(): string
18+
{
19+
return Node\Expr\MethodCall::class;
20+
}
21+
22+
public function processNode(Node $node, Scope $scope): array
23+
{
24+
/** @var Node\Expr\MethodCall $node */
25+
$node = $node;
26+
27+
if (!$node->name instanceof Node\Identifier || $node->name->name !== 'method') {
28+
return [];
29+
}
30+
31+
if (count($node->args) < 1) {
32+
return [];
33+
}
34+
35+
$method = $node->args[0]->value->value;
36+
$type = $scope->getType($node->var);
37+
38+
if (
39+
$type instanceof IntersectionType
40+
&& $type->isSubTypeOf(new ObjectType(MockObject::class))
41+
&& !$type->hasMethod($method)->yes()
42+
) {
43+
return [
44+
sprintf(
45+
'Trying to mock an undefined method %s() on class %s.',
46+
$method,
47+
\implode('&', $type->getReferencedClasses())
48+
),
49+
];
50+
}
51+
52+
return [];
53+
}
54+
55+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace PHPStan\Rules\PHPUnit;
4+
5+
use PHPStan\Rules\Rule;
6+
7+
/**
8+
* @extends \PHPStan\Testing\RuleTestCase<MockMethodCallRule>
9+
*/
10+
class MockMethodCallRuleTest extends \PHPStan\Testing\RuleTestCase
11+
{
12+
13+
protected function getRule(): Rule
14+
{
15+
return new MockMethodCallRule();
16+
}
17+
18+
public function testRule(): void
19+
{
20+
$this->analyse([__DIR__ . '/data/mock-method-call.php'], [
21+
[
22+
'Trying to mock an undefined method doBadThing() on class MockMethodCall\Bar&PHPUnit\Framework\MockObject\MockObject.',
23+
15,
24+
],
25+
[
26+
'Trying to mock an undefined method doBadThing() on class MockMethodCall\Bar&PHPUnit\Framework\MockObject\MockObject.',
27+
20,
28+
],
29+
]);
30+
}
31+
32+
/**
33+
* @return string[]
34+
*/
35+
public static function getAdditionalConfigFiles(): array
36+
{
37+
return [
38+
__DIR__ . '/../../../extension.neon',
39+
];
40+
}
41+
42+
}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace MockMethodCall;
4+
5+
class Foo extends \PHPUnit\Framework\TestCase
6+
{
7+
8+
public function testGoodMethod()
9+
{
10+
$this->createMock(Bar::class)->method('doThing');
11+
}
12+
13+
public function testBadMethod()
14+
{
15+
$this->createMock(Bar::class)->method('doBadThing');
16+
}
17+
18+
public function testBadMethodWithExpectation()
19+
{
20+
$this->createMock(Bar::class)->expects($this->once())->method('doBadThing');
21+
}
22+
23+
public function testWithAnotherObject()
24+
{
25+
$bar = new BarWithMethod();
26+
$bar->method('doBadThing');
27+
}
28+
29+
}
30+
31+
class Bar {
32+
public function doThing()
33+
{
34+
return 1;
35+
}
36+
};
37+
38+
class BarWithMethod {
39+
public function method(string $string)
40+
{
41+
return $string;
42+
}
43+
};

0 commit comments

Comments
 (0)