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([ diff --git a/.psalm/baseline.xml b/.psalm/baseline.xml index 2c547b03e9e..e954e170d29 100644 --- a/.psalm/baseline.xml +++ b/.psalm/baseline.xml @@ -1,5 +1,5 @@ - + $expectedElement->childNodes->item($i) @@ -397,9 +397,7 @@ - - $split[0] - $split[0] + $className @@ -455,18 +453,14 @@ - + $className - $pieces[0] $className $className $dataProviderClassName - + $e - $operator - $operator - $operator diff --git a/src/Framework/MockObject/Builder/InvocationMocker.php b/src/Framework/MockObject/Builder/InvocationMocker.php index 05f5fb577d2..48576615886 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 @@ -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..6d6508bc6b7 --- /dev/null +++ b/src/Framework/MockObject/Builder/InvocationStubber.php @@ -0,0 +1,62 @@ + + * + * 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 +{ + public function will(Stub $stub): Identity; + + /** @return self */ + public function willReturn($value, ...$nextValues) /*: self */; + + /** + * @param mixed $reference + * + * @return self + */ + public function willReturnReference(&$reference) /*: self */; + + /** + * @param array> $valueMap + * + * @return self + */ + public function willReturnMap(array $valueMap) /*: self */; + + /** + * @param int $argumentIndex + * + * @return self + */ + public function willReturnArgument($argumentIndex) /*: self */; + + /** + * @param callable $callback + * + * @return self + */ + public function willReturnCallback($callback) /*: self */; + + /** @return self */ + public function willReturnSelf() /*: self */; + + /** + * @param mixed $values + * + * @return self + */ + public function willReturnOnConsecutiveCalls(...$values) /*: self */; + + /** @return 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..14e0bff2ff2 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; 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]);