Skip to content

Commit

Permalink
Throw exception on non unique mocked method
Browse files Browse the repository at this point in the history
This patch will make a mock throw an exception when multiple matchers
can be applied to a invoke. When allowing this, results of tests are
not predictable.

refs #4255
  • Loading branch information
jaapio authored and sebastianbergmann committed Jun 3, 2020
1 parent 332cca4 commit 7f6d2bb
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 22 deletions.
48 changes: 26 additions & 22 deletions src/Framework/MockObject/InvocationHandler.php
Expand Up @@ -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) {
Expand Down Expand Up @@ -186,6 +167,29 @@ public function verify(): void
}
}

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;
}

private function addMatcher(Matcher $matcher): void
{
$this->matchers[] = $matcher;
Expand Down
35 changes: 35 additions & 0 deletions tests/unit/Framework/MockObject/InvocationHandlerTest.php
Expand Up @@ -9,6 +9,7 @@
*/
namespace PHPUnit\Framework\MockObject;

use PHPUnit\Framework\ExpectationFailedException;
use PHPUnit\Framework\TestCase;

class InvocationHandlerTest extends TestCase
Expand All @@ -23,4 +24,38 @@ public function testExceptionThrownIn__ToStringIsDeferred(): void
$this->expectExceptionMessage('planned error');
$mock->__toString();
}

public function testSingleMatcherIsHandled(): void
{
$mock = $this->getMockBuilder(\stdClass::class)
->addMethods(['foo'])
->getMock();

$mock->expects($this->once())
->method('foo')
->willReturn('result');

$this->assertSame('result', $mock->foo());
}

public function testNonUniqueMatchThrowsException(): void
{
$mock = $this->getMockBuilder(\stdClass::class)
->addMethods(['foo'])
->getMock();

$mock->expects($this->any())
->method($this->stringStartsWith('foo'))
->willReturn('result');

$mock->expects($this->any())
->method('foo')
->with('bar')
->willReturn('result');

$this->expectException(ExpectationFailedException::class);
$this->expectExceptionMessage('Non unique mocked method invocation: stdClass::foo');

$mock->foo();
}
}

0 comments on commit 7f6d2bb

Please sign in to comment.