From 1bf0758d8b6f237b6aea2c5589fb8b91627ccfbc Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 6 Sep 2019 11:01:38 +0200 Subject: [PATCH 01/10] #3120 implemented `TestCase#createStub()` --- .../MockObject/Builder/InvocationMocker.php | 12 ++--- .../MockObject/Builder/InvocationStubber.php | 44 +++++++++++++++++++ src/Framework/MockObject/Stub.php | 4 +- src/Framework/TestCase.php | 13 ++++++ tests/static-analysis/TestUsingMocks.php | 11 +++++ tests/unit/Framework/TestCaseTest.php | 36 +++++++++++++-- 6 files changed, 107 insertions(+), 13 deletions(-) create mode 100644 src/Framework/MockObject/Builder/InvocationStubber.php diff --git a/src/Framework/MockObject/Builder/InvocationMocker.php b/src/Framework/MockObject/Builder/InvocationMocker.php index 05f5fb577d2..914c730e9a7 100644 --- a/src/Framework/MockObject/Builder/InvocationMocker.php +++ b/src/Framework/MockObject/Builder/InvocationMocker.php @@ -29,7 +29,7 @@ /** * @internal This class is not covered by the backward compatibility promise for PHPUnit */ -final class InvocationMocker implements MethodNameMatch +final class InvocationMocker implements InvocationStubber, MethodNameMatch { /** * @var MatcherCollection @@ -68,7 +68,7 @@ public function id($id): self return $this; } - public function will(Stub $stub): Identity + public function will(Stub $stub): self { $this->matcher->setStub($stub); @@ -92,9 +92,7 @@ public function willReturn($value, ...$nextValues): self return $this->will($stub); } - /** - * @param mixed $reference - */ + /** {@inheritDoc} */ public function willReturnReference(&$reference): self { $stub = new ReturnReference($reference); @@ -116,9 +114,7 @@ public function willReturnArgument($argumentIndex): self return $this->will($stub); } - /** - * @param callable $callback - */ + /** {@inheritDoc} */ public function willReturnCallback($callback): self { $stub = new ReturnCallback($callback); diff --git a/src/Framework/MockObject/Builder/InvocationStubber.php b/src/Framework/MockObject/Builder/InvocationStubber.php new file mode 100644 index 00000000000..4acfad9f4f1 --- /dev/null +++ b/src/Framework/MockObject/Builder/InvocationStubber.php @@ -0,0 +1,44 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject\Builder; + +use PHPUnit\Framework\MockObject\Stub\Stub; + +/** @internal This class is not covered by the backward compatibility promise for PHPUnit */ +interface InvocationStubber +{ + /** + * @TODO is "will" actually sensible in a stub context? Should a stub produce side-effects? + * + * Note: probably yes, since you want a stub of a promise to be able to resolve a real callback, for example + */ + public function will(Stub $stub): Identity; + + public function willReturn($value, ...$nextValues): self; + + /** @param mixed $reference */ + public function willReturnReference(&$reference): self; + + /** @param array> $valueMap */ + public function willReturnMap(array $valueMap): self; + + /** @param int $argumentIndex */ + public function willReturnArgument($argumentIndex): self; + + /** @param callable $callback */ + public function willReturnCallback($callback): self; + + public function willReturnSelf(): self; + + /** @param mixed $values */ + public function willReturnOnConsecutiveCalls(...$values): self; + + public function willThrowException(\Throwable $exception): self; +} diff --git a/src/Framework/MockObject/Stub.php b/src/Framework/MockObject/Stub.php index e5c88274916..951885ed1f7 100644 --- a/src/Framework/MockObject/Stub.php +++ b/src/Framework/MockObject/Stub.php @@ -9,10 +9,10 @@ */ namespace PHPUnit\Framework\MockObject; -use PHPUnit\Framework\MockObject\Builder\InvocationMocker as BuilderInvocationMocker; +use PHPUnit\Framework\MockObject\Builder\InvocationStubber; /** - * @method BuilderInvocationMocker method($constraint) + * @method InvocationStubber method($constraint) */ interface Stub { diff --git a/src/Framework/TestCase.php b/src/Framework/TestCase.php index 6791df30066..f487e46db2f 100644 --- a/src/Framework/TestCase.php +++ b/src/Framework/TestCase.php @@ -27,6 +27,7 @@ use PHPUnit\Framework\MockObject\Matcher\InvokedCount as InvokedCountMatcher; use PHPUnit\Framework\MockObject\MockBuilder; use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Framework\MockObject\Stub\ConsecutiveCalls as ConsecutiveCallsStub; use PHPUnit\Framework\MockObject\Stub\Exception as ExceptionStub; use PHPUnit\Framework\MockObject\Stub\ReturnArgument as ReturnArgumentStub; @@ -1543,6 +1544,18 @@ protected function setLocale(...$args): void } } + /** + * Makes configurable stub for the specified class. + * + * @psalm-template RealInstanceType of object + * @psalm-param class-string $originalClassName + * @psalm-return Stub&RealInstanceType + */ + protected function createStub(string $originalClassName): Stub + { + return $this->createMock($originalClassName); + } + /** * Returns a test double for the specified class. * diff --git a/tests/static-analysis/TestUsingMocks.php b/tests/static-analysis/TestUsingMocks.php index 8f91d85f00d..2ed4a72394f 100644 --- a/tests/static-analysis/TestUsingMocks.php +++ b/tests/static-analysis/TestUsingMocks.php @@ -35,6 +35,17 @@ public function testWillSayHelloThroughCreateMock(): void self::assertSame('hello mock!', $mock->sayHello()); } + public function testWillSayHelloThroughCreateStub(): void + { + $mock = $this->createStub(HelloWorldClass::class); + + $mock + ->method('sayHello') + ->willReturn('hello mock!'); + + self::assertSame('hello mock!', $mock->sayHello()); + } + public function testWillSayHelloThroughCreateConfiguredMock(): void { $mock = $this->createConfiguredMock(HelloWorldClass::class, []); diff --git a/tests/unit/Framework/TestCaseTest.php b/tests/unit/Framework/TestCaseTest.php index 5108d187672..4d5ecc586ed 100644 --- a/tests/unit/Framework/TestCaseTest.php +++ b/tests/unit/Framework/TestCaseTest.php @@ -10,6 +10,7 @@ namespace PHPUnit\Framework; use PHPUnit\Framework\MockObject\MockObject; +use PHPUnit\Framework\MockObject\Stub\Stub; use PHPUnit\Runner\BaseTestRunner; use PHPUnit\Util\Test as TestUtil; @@ -832,13 +833,28 @@ public function testCreateMockFromClassName(): void public function testCreateMockMocksAllMethods(): void { - /** @var \Mockable $mock */ $mock = $this->createMock(\Mockable::class); $this->assertNull($mock->mockableMethod()); $this->assertNull($mock->anotherMockableMethod()); } + public function testCreateStubFromClassName(): void + { + $mock = $this->createStub(\Mockable::class); + + $this->assertInstanceOf(\Mockable::class, $mock); + $this->assertInstanceOf(Stub::class, $mock); + } + + public function testCreateStubMocksAllMethods(): void + { + $mock = $this->createStub(\Mockable::class); + + $this->assertNull($mock->mockableMethod()); + $this->assertNull($mock->anotherMockableMethod()); + } + public function testCreatePartialMockDoesNotMockAllMethods(): void { /** @var \Mockable $mock */ @@ -879,7 +895,6 @@ public function testCreatePartialMockWithRealMethods(): void public function testCreateMockSkipsConstructor(): void { - /** @var \Mockable $mock */ $mock = $this->createMock(\Mockable::class); $this->assertNull($mock->constructorArgs); @@ -887,13 +902,27 @@ public function testCreateMockSkipsConstructor(): void public function testCreateMockDisablesOriginalClone(): void { - /** @var \Mockable $mock */ $mock = $this->createMock(\Mockable::class); $cloned = clone $mock; $this->assertNull($cloned->cloned); } + public function testCreateStubSkipsConstructor(): void + { + $mock = $this->createStub(\Mockable::class); + + $this->assertNull($mock->constructorArgs); + } + + public function testCreateStubDisablesOriginalClone(): void + { + $mock = $this->createStub(\Mockable::class); + + $cloned = clone $mock; + $this->assertNull($cloned->cloned); + } + public function testConfiguredMockCanBeCreated(): void { /** @var \Mockable $mock */ @@ -924,6 +953,7 @@ public function testProvidingArrayThatMixesObjectsAndScalars(): void [123], ['foo'], [$this->createMock(\Mockable::class)], + [$this->createStub(\Mockable::class)], ]; $test = new \TestAutoreferenced('testJsonEncodeException', [$data]); From 9e8b320ef7f122cfd99f650d35b5297c8f18d8e3 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 6 Sep 2019 11:04:10 +0200 Subject: [PATCH 02/10] #3120 removed fixed baseline issues --- .psalm/baseline.xml | 34 ++++------------------------------ 1 file changed, 4 insertions(+), 30 deletions(-) diff --git a/.psalm/baseline.xml b/.psalm/baseline.xml index 2c547b03e9e..f2921f58dd7 100644 --- a/.psalm/baseline.xml +++ b/.psalm/baseline.xml @@ -1,5 +1,5 @@ - + $expectedElement->childNodes->item($i) @@ -97,26 +97,6 @@ self - - $this->will($stub) - $this->will($stub) - $this->will($stub) - $this->will($stub) - $this->will($stub) - $this->will($stub) - $this->will($stub) - $this->will($stub) - - - self - self - self - self - self - self - self - self - registerId @@ -397,9 +377,7 @@ - - $split[0] - $split[0] + $className @@ -455,18 +433,14 @@ - + $className - $pieces[0] $className $className $dataProviderClassName - + $e - $operator - $operator - $operator From 77ec32c593dab233285d913e7584eb85083c6117 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 6 Sep 2019 11:06:30 +0200 Subject: [PATCH 03/10] #3120 marked `InvocationMocker#will() : self` return type mismatch as false positive The method `InvocationMocker#will()` is LSP-compliant with the parent types, but a bug in psalm reports it as a false positive. Ref: https://github.com/vimeo/psalm/issues/2102 --- .psalm/baseline.xml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.psalm/baseline.xml b/.psalm/baseline.xml index f2921f58dd7..809e61c6d83 100644 --- a/.psalm/baseline.xml +++ b/.psalm/baseline.xml @@ -97,6 +97,9 @@ self + + InvocationMocker + registerId From 03ad72cddd17e7ac6f38533ce1deaa93fc324029 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 6 Sep 2019 11:10:59 +0200 Subject: [PATCH 04/10] #3120 reverted `InvocationMocker#will() : self` - only works on PHP 7.4 I was too eager in using an appropriate type declaration on `InvocationMocker`, but that has to wait for newer PHP releases. --- .psalm/baseline.xml | 20 +++++++++++++++++++ .../MockObject/Builder/InvocationMocker.php | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/.psalm/baseline.xml b/.psalm/baseline.xml index 809e61c6d83..cfe53c3c1dc 100644 --- a/.psalm/baseline.xml +++ b/.psalm/baseline.xml @@ -97,9 +97,29 @@ self + + $this->will($stub) + $this->will($stub) + $this->will($stub) + $this->will($stub) + $this->will($stub) + $this->will($stub) + $this->will($stub) + $this->will($stub) + InvocationMocker + + self + self + self + self + self + self + self + self + registerId diff --git a/src/Framework/MockObject/Builder/InvocationMocker.php b/src/Framework/MockObject/Builder/InvocationMocker.php index 914c730e9a7..48576615886 100644 --- a/src/Framework/MockObject/Builder/InvocationMocker.php +++ b/src/Framework/MockObject/Builder/InvocationMocker.php @@ -68,7 +68,7 @@ public function id($id): self return $this; } - public function will(Stub $stub): self + public function will(Stub $stub): Identity { $this->matcher->setStub($stub); From 9398dfc905ce300ba9d580a52ff6aa3da577a6a1 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 6 Sep 2019 11:24:54 +0200 Subject: [PATCH 05/10] #3120 removing `self` types on `InvocationStubber`: requires PHP 7.4 Once we have PHP 7.4, we can move these type declarations into the interface signature again, but for now, the existing inheritance leads to a crash on PHP < 7.4. --- .../MockObject/Builder/InvocationStubber.php | 49 ++++++++++++++----- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/Framework/MockObject/Builder/InvocationStubber.php b/src/Framework/MockObject/Builder/InvocationStubber.php index 4acfad9f4f1..82ed97f3d23 100644 --- a/src/Framework/MockObject/Builder/InvocationStubber.php +++ b/src/Framework/MockObject/Builder/InvocationStubber.php @@ -21,24 +21,47 @@ interface InvocationStubber */ public function will(Stub $stub): Identity; - public function willReturn($value, ...$nextValues): self; + /** @return self */ + public function willReturn($value, ...$nextValues); - /** @param mixed $reference */ - public function willReturnReference(&$reference): self; + /** + * @param mixed $reference + * + * @return self + */ + public function willReturnReference(&$reference); - /** @param array> $valueMap */ - public function willReturnMap(array $valueMap): self; + /** + * @param array> $valueMap + * + * @return self + */ + public function willReturnMap(array $valueMap); - /** @param int $argumentIndex */ - public function willReturnArgument($argumentIndex): self; + /** + * @param int $argumentIndex + * + * @return self + */ + public function willReturnArgument($argumentIndex); - /** @param callable $callback */ - public function willReturnCallback($callback): self; + /** + * @param callable $callback + * + * @return self + */ + public function willReturnCallback($callback); - public function willReturnSelf(): self; + /** @return self */ + public function willReturnSelf(); - /** @param mixed $values */ - public function willReturnOnConsecutiveCalls(...$values): self; + /** + * @param mixed $values + * + * @return self + */ + public function willReturnOnConsecutiveCalls(...$values); - public function willThrowException(\Throwable $exception): self; + /** @return self */ + public function willThrowException(\Throwable $exception); } From bcd9c10a384d2a3c7d637ee1b74f658a386412b4 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 6 Sep 2019 11:25:47 +0200 Subject: [PATCH 06/10] #3120 corrected `Stub` reference: was pointing to method instead of class stub --- tests/unit/Framework/TestCaseTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unit/Framework/TestCaseTest.php b/tests/unit/Framework/TestCaseTest.php index 4d5ecc586ed..14e0bff2ff2 100644 --- a/tests/unit/Framework/TestCaseTest.php +++ b/tests/unit/Framework/TestCaseTest.php @@ -10,7 +10,7 @@ namespace PHPUnit\Framework; use PHPUnit\Framework\MockObject\MockObject; -use PHPUnit\Framework\MockObject\Stub\Stub; +use PHPUnit\Framework\MockObject\Stub; use PHPUnit\Runner\BaseTestRunner; use PHPUnit\Util\Test as TestUtil; From 5cd9bec780bebe32980f974cc4acf5200089919e Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 6 Sep 2019 11:29:07 +0200 Subject: [PATCH 07/10] #3120 removed `InvocationMocker` inheritance mismatch, fixed --- .psalm/baseline.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/.psalm/baseline.xml b/.psalm/baseline.xml index cfe53c3c1dc..e954e170d29 100644 --- a/.psalm/baseline.xml +++ b/.psalm/baseline.xml @@ -107,9 +107,6 @@ $this->will($stub) $this->will($stub) - - InvocationMocker - self self From 315d74eacc9f3ea1f01515d7f3690ffa7a1b4425 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 6 Sep 2019 11:57:38 +0200 Subject: [PATCH 08/10] #3120 we will keep `InvocationStubber#will()` - useful for callbacks and such --- src/Framework/MockObject/Builder/InvocationStubber.php | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/Framework/MockObject/Builder/InvocationStubber.php b/src/Framework/MockObject/Builder/InvocationStubber.php index 82ed97f3d23..93fcd4e97ff 100644 --- a/src/Framework/MockObject/Builder/InvocationStubber.php +++ b/src/Framework/MockObject/Builder/InvocationStubber.php @@ -14,11 +14,6 @@ /** @internal This class is not covered by the backward compatibility promise for PHPUnit */ interface InvocationStubber { - /** - * @TODO is "will" actually sensible in a stub context? Should a stub produce side-effects? - * - * Note: probably yes, since you want a stub of a promise to be able to resolve a real callback, for example - */ public function will(Stub $stub): Identity; /** @return self */ From 958297d13033341f061aba51ca8a2e9578fd7c07 Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 6 Sep 2019 11:58:40 +0200 Subject: [PATCH 09/10] #3120 pseudo-docs for `:self` for when we can upgrade to PHP 7.4 `InvocationStubber` methods cannot have return type declarations until then. --- .../MockObject/Builder/InvocationStubber.php | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/Framework/MockObject/Builder/InvocationStubber.php b/src/Framework/MockObject/Builder/InvocationStubber.php index 93fcd4e97ff..6d6508bc6b7 100644 --- a/src/Framework/MockObject/Builder/InvocationStubber.php +++ b/src/Framework/MockObject/Builder/InvocationStubber.php @@ -17,46 +17,46 @@ interface InvocationStubber public function will(Stub $stub): Identity; /** @return self */ - public function willReturn($value, ...$nextValues); + public function willReturn($value, ...$nextValues) /*: self */; /** * @param mixed $reference * * @return self */ - public function willReturnReference(&$reference); + public function willReturnReference(&$reference) /*: self */; /** * @param array> $valueMap * * @return self */ - public function willReturnMap(array $valueMap); + public function willReturnMap(array $valueMap) /*: self */; /** * @param int $argumentIndex * * @return self */ - public function willReturnArgument($argumentIndex); + public function willReturnArgument($argumentIndex) /*: self */; /** * @param callable $callback * * @return self */ - public function willReturnCallback($callback); + public function willReturnCallback($callback) /*: self */; /** @return self */ - public function willReturnSelf(); + public function willReturnSelf() /*: self */; /** * @param mixed $values * * @return self */ - public function willReturnOnConsecutiveCalls(...$values); + public function willReturnOnConsecutiveCalls(...$values) /*: self */; /** @return self */ - public function willThrowException(\Throwable $exception); + public function willThrowException(\Throwable $exception) /*: self */; } From 2894f1e5eb2cd88708fdba608718e5b6a07391aa Mon Sep 17 00:00:00 2001 From: Marco Pivetta Date: Fri, 6 Sep 2019 12:04:57 +0200 Subject: [PATCH 10/10] #3120 added `.phpstorm.meta.php` entry for `TestCase#createStub()` --- .phpstorm.meta.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.phpstorm.meta.php b/.phpstorm.meta.php index cac21bdd51d..5e4c4c2d7dd 100644 --- a/.phpstorm.meta.php +++ b/.phpstorm.meta.php @@ -8,6 +8,13 @@ ]) ); + override( + \PHPUnit\Framework\TestCase::createStub(0), + map([ + '@&\PHPUnit\Framework\MockObject\Stub', + ]) + ); + override( \PHPUnit\Framework\TestCase::createConfiguredMock(0), map([