diff --git a/src/Framework/MockObject/InvocationHandler.php b/src/Framework/MockObject/InvocationHandler.php index 7b2b2bb7493..5c5ddb712e8 100644 --- a/src/Framework/MockObject/InvocationHandler.php +++ b/src/Framework/MockObject/InvocationHandler.php @@ -113,30 +113,11 @@ public function expects(InvocationOrder $rule): InvocationMocker public function invoke(Invocation $invocation) { $exception = null; - $hasReturnValue = false; $returnValue = null; + $match = $this->findMatcher($invocation); - foreach ($this->matchers as $match) { - try { - if ($match->matches($invocation)) { - $value = $match->invoked($invocation); - - if (!$hasReturnValue) { - $returnValue = $value; - $hasReturnValue = true; - } - } - } catch (\Exception $e) { - $exception = $e; - } - } - - if ($exception !== null) { - throw $exception; - } - - if ($hasReturnValue) { - return $returnValue; + if ($match !== null) { + return $match->invoked($invocation); } if (!$this->returnValueGeneration) { @@ -160,6 +141,28 @@ public function invoke(Invocation $invocation) return $invocation->generateReturnValue(); } + private function findMatcher(Invocation $invocation): ?Matcher + { + $result = []; + foreach ($this->matchers as $matcher) { + if ($matcher->matches($invocation)) { + $result[] = $matcher; + } + } + + if (count($result) > 1) { + throw new ExpectationFailedException( + sprintf( + 'Non unique mocked method invocation: %s::%s', + $invocation->getClassName(), + $invocation->getMethodName() + ) + ); + } + + return current($result) ?: null; + } + public function matches(Invocation $invocation): bool { foreach ($this->matchers as $matcher) { diff --git a/tests/unit/Framework/MockObject/Builder/InvocationMockerTest.php b/tests/unit/Framework/MockObject/Builder/InvocationMockerTest.php index 429e35318c2..7d334e0fd1e 100644 --- a/tests/unit/Framework/MockObject/Builder/InvocationMockerTest.php +++ b/tests/unit/Framework/MockObject/Builder/InvocationMockerTest.php @@ -9,6 +9,7 @@ * file that was distributed with this source code. */ use PHPUnit\Framework\Constraint\IsEqual; +use PHPUnit\Framework\ExpectationFailedException; use PHPUnit\Framework\MockObject\Builder\InvocationMocker; use PHPUnit\Framework\MockObject\IncompatibleReturnValueException; use PHPUnit\Framework\MockObject\InvocationHandler; @@ -257,4 +258,26 @@ public function testWillReturnAlreadyInstantiatedStubs(): void $this->assertSame('foo', $mock->foo()); $this->assertSame($mock, $mock->bar()); } + + public function testMultipleWithParametersWillReturnLatestDefined(): void + { + $mock = $this->getMockBuilder(stdClass::class) + ->setMethods(['foo']) + ->getMock(); + + $mock->expects($this->any()) + ->method('foo') + ->with('bar') + ->willReturn('first'); + + $mock->expects($this->any()) + ->method('foo') + ->with('foo') + ->willReturn('second'); + + $this->expectException(ExpectationFailedException::class); + $this->getExpectedExceptionMessage('Non unique mocked method invocation: stdClass::foo'); + + $mock->foo('bar'); + } }