From cff3c4a5c4e457ccf8efaa90bb050d8544c9d0d7 Mon Sep 17 00:00:00 2001 From: Sebastian Bergmann Date: Wed, 31 Jul 2024 16:45:29 +0200 Subject: [PATCH 1/2] Add test that characterizes the current behaviour --- .../Framework/MockObject/TestDoubleTestCase.php | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/unit/Framework/MockObject/TestDoubleTestCase.php b/tests/unit/Framework/MockObject/TestDoubleTestCase.php index a8366f9102..dd142280d1 100644 --- a/tests/unit/Framework/MockObject/TestDoubleTestCase.php +++ b/tests/unit/Framework/MockObject/TestDoubleTestCase.php @@ -18,6 +18,7 @@ use PHPUnit\TestFixture\MockObject\InterfaceWithNeverReturningMethod; use PHPUnit\TestFixture\MockObject\InterfaceWithReturnTypeDeclaration; use stdClass; +use TypeError; abstract class TestDoubleTestCase extends TestCase { @@ -188,6 +189,20 @@ final public function testMethodCanBeConfiguredToReturnDifferentValuesOnConsecut $this->assertTrue($double->doSomething()); } + final public function testMethodConfiguredToReturnDifferentValuesOnConsecutiveCallsCannotBeCalledMoreOftenThanReturnValuesHaveBeenConfigured(): void + { + $double = $this->createTestDouble(InterfaceWithReturnTypeDeclaration::class); + + $double->method('doSomething')->willReturn(false, true); + + $this->assertFalse($double->doSomething()); + $this->assertTrue($double->doSomething()); + + $this->expectException(TypeError::class); + + $double->doSomething(); + } + final public function testMethodCanBeConfiguredToReturnDifferentValuesAndThrowExceptionsOnConsecutiveCalls(): void { $double = $this->createTestDouble(InterfaceWithReturnTypeDeclaration::class); From 490879817a1417fd5fa1149a47b6f2f1b70ada6a Mon Sep 17 00:00:00 2001 From: Sebastian Bergmann Date: Wed, 31 Jul 2024 16:56:27 +0200 Subject: [PATCH 2/2] Improve error message --- .../NoMoreReturnValuesConfiguredException.php | 32 +++++++++++++++++++ .../Runtime/Stub/ConsecutiveCalls.php | 16 +++++++++- .../MockObject/TestDoubleTestCase.php | 4 +-- 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 src/Framework/MockObject/Exception/NoMoreReturnValuesConfiguredException.php diff --git a/src/Framework/MockObject/Exception/NoMoreReturnValuesConfiguredException.php b/src/Framework/MockObject/Exception/NoMoreReturnValuesConfiguredException.php new file mode 100644 index 0000000000..c4b181653f --- /dev/null +++ b/src/Framework/MockObject/Exception/NoMoreReturnValuesConfiguredException.php @@ -0,0 +1,32 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ +namespace PHPUnit\Framework\MockObject; + +use function sprintf; + +/** + * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit + * + * @internal This class is not covered by the backward compatibility promise for PHPUnit + */ +final class NoMoreReturnValuesConfiguredException extends \PHPUnit\Framework\Exception implements Exception +{ + public function __construct(Invocation $invocation, int $numberOfConfiguredReturnValues) + { + parent::__construct( + sprintf( + 'Only %d return values have been configured for %s::%s()', + $numberOfConfiguredReturnValues, + $invocation->className(), + $invocation->methodName(), + ), + ); + } +} diff --git a/src/Framework/MockObject/Runtime/Stub/ConsecutiveCalls.php b/src/Framework/MockObject/Runtime/Stub/ConsecutiveCalls.php index 14b91fe356..7b1297de5b 100644 --- a/src/Framework/MockObject/Runtime/Stub/ConsecutiveCalls.php +++ b/src/Framework/MockObject/Runtime/Stub/ConsecutiveCalls.php @@ -10,7 +10,9 @@ namespace PHPUnit\Framework\MockObject\Stub; use function array_shift; +use function count; use PHPUnit\Framework\MockObject\Invocation; +use PHPUnit\Framework\MockObject\NoMoreReturnValuesConfiguredException; /** * @no-named-arguments Parameter names are not covered by the backward compatibility promise for PHPUnit @@ -20,14 +22,26 @@ final class ConsecutiveCalls implements Stub { private array $stack; + private int $numberOfConfiguredReturnValues; public function __construct(array $stack) { - $this->stack = $stack; + $this->stack = $stack; + $this->numberOfConfiguredReturnValues = count($stack); } + /** + * @throws NoMoreReturnValuesConfiguredException + */ public function invoke(Invocation $invocation): mixed { + if (empty($this->stack)) { + throw new NoMoreReturnValuesConfiguredException( + $invocation, + $this->numberOfConfiguredReturnValues, + ); + } + $value = array_shift($this->stack); if ($value instanceof Stub) { diff --git a/tests/unit/Framework/MockObject/TestDoubleTestCase.php b/tests/unit/Framework/MockObject/TestDoubleTestCase.php index dd142280d1..c883bd53cc 100644 --- a/tests/unit/Framework/MockObject/TestDoubleTestCase.php +++ b/tests/unit/Framework/MockObject/TestDoubleTestCase.php @@ -18,7 +18,6 @@ use PHPUnit\TestFixture\MockObject\InterfaceWithNeverReturningMethod; use PHPUnit\TestFixture\MockObject\InterfaceWithReturnTypeDeclaration; use stdClass; -use TypeError; abstract class TestDoubleTestCase extends TestCase { @@ -198,7 +197,8 @@ final public function testMethodConfiguredToReturnDifferentValuesOnConsecutiveCa $this->assertFalse($double->doSomething()); $this->assertTrue($double->doSomething()); - $this->expectException(TypeError::class); + $this->expectException(NoMoreReturnValuesConfiguredException::class); + $this->expectExceptionMessage('Only 2 return values have been configured for PHPUnit\TestFixture\MockObject\InterfaceWithReturnTypeDeclaration::doSomething()'); $double->doSomething(); }