diff --git a/.gitignore b/.gitignore index e26f45e..d74ea1b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ composer.phar /vendor/ composer.lock +.php-version diff --git a/stubs/Mockery.php b/stubs/Mockery.php index ba62263..3cc4131 100644 --- a/stubs/Mockery.php +++ b/stubs/Mockery.php @@ -55,9 +55,6 @@ public static function notAnyOf(...$args) namespace Mockery { - use Mockery\HigherOrderMessage; - use Mockery\MockInterface; - use Mockery\ExpectsHigherOrderMessage; use Mockery\Exception\BadMethodCallException; class Mock implements MockInterface @@ -99,13 +96,13 @@ interface MockInterface * * @param mixed ...$methodNames one or many methods that are expected to be called in this mock * - * @return \Mockery\ExpectationInterface&static + * @return ExpectationInterface&static */ public function shouldReceive(...$methodNames); /** - * @param mixed $something String method name (optional) - * @return \Mockery\ExpectationInterface&static + * @param mixed $something + * @return ($something is string ? ExpectationInterface : ExpectsHigherOrderMessage) */ public function expects($something = null); @@ -114,7 +111,7 @@ public function expects($something = null); * * @param mixed ...$methodNames one or many methods that are expected to be called in this mock * - * @return \Mockery\ExpectationInterface&static + * @return ExpectationInterface&static */ public function shouldNotReceive(...$methodNames); @@ -127,6 +124,12 @@ public function makePartial(); * @return static */ public function shouldAllowMockingProtectedMethods(); + + /** + * @param mixed $something + * @return ($something is string ? ExpectationInterface : ($something is list{} ? HigherOrderMessage : static)) + */ + public function allows($something = []); } /** @@ -140,6 +143,12 @@ interface ExpectationInterface */ public function andReturn(...$args); + /** + * @param mixed ...$args + * @return static + */ + public function andReturns(...$args); + /** * Set a sequential queue of return values with an array * @@ -290,6 +299,47 @@ public function withArgs($argsOrClosure) { } } + + /** + * we need a docblock here, otherwise Psalm looks into original one, + * sees method tag there and starts emitting UndefinedMagicMethod + */ + class HigherOrderMessage + { + public function __construct(MockInterface $mock, $method) + { + } + + public function withArgs(\Closure|array $args): Expectation + { + } + + /** + * @return \Mockery\Expectation + */ + public function __call($method, $args) + { + } + } + + /** + * we need a docblock here, otherwise Psalm looks into original one, sees + * that it extends HigherOrderMessage, looks into original + * HigherOrderMessagemethod dockblock, sees method tag there and starts + * emitting UndefinedMagicMethod + */ + class ExpectsHigherOrderMessage extends HigherOrderMessage + { + public function __construct(MockInterface $mock) + { + } + /** + * @return \Mockery\Expectation + */ + public function __call($method, $args) + { + } + } } // phpcs:enable diff --git a/tests/acceptance/MockReturn.feature b/tests/acceptance/MockReturn.feature index fc7be6d..0ac59e2 100644 --- a/tests/acceptance/MockReturn.feature +++ b/tests/acceptance/MockReturn.feature @@ -104,3 +104,37 @@ Feature: MockReturn """ When I run Psalm Then I see no errors + + Scenario: New style stubbing + Given I have the following code + """ + class User + { + public function doThings(): int { return 10; } + } + + function f(): int { + $mock = Mockery::mock(User::class); + $mock->allows()->doThings()->andReturns(1); + return $mock->doThings(); + } + """ + When I run Psalm + Then I see no errors + + Scenario: New style mocking + Given I have the following code + """ + class User + { + public function doThings(): int { return 10; } + } + + function f(): int { + $mock = Mockery::mock(User::class); + $mock->expects()->doThings()->andReturns(1); + return $mock->doThings(); + } + """ + When I run Psalm + Then I see no errors