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 sebastianbergmann#4255
  • Loading branch information
jaapio committed Jun 1, 2020
1 parent 61f7c75 commit 1ce8bfb
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 22 deletions.
47 changes: 25 additions & 22 deletions src/Framework/MockObject/InvocationHandler.php
Original file line number Diff line number Diff line change
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 All @@ -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) {
Expand Down
23 changes: 23 additions & 0 deletions tests/unit/Framework/MockObject/Builder/InvocationMockerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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');
}
}

0 comments on commit 1ce8bfb

Please sign in to comment.