File tree Expand file tree Collapse file tree 3 files changed +140
-0
lines changed Expand file tree Collapse file tree 3 files changed +140
-0
lines changed Original file line number Diff line number Diff line change
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
+ }
Original file line number Diff line number Diff line change
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
+ }
Original file line number Diff line number Diff line change
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
+ };
You can’t perform that action at this time.
0 commit comments