Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Revert exception debugging #1032

Merged
merged 2 commits into from Dec 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
3 changes: 3 additions & 0 deletions CHANGELOG.md
@@ -1,5 +1,8 @@
# Change Log

## 1.3.1 (2019-12-26)
* Revert improved exception debugging due to BC breaks (#1032)

## 1.3.0 (2019-11-24)

* Added capture `Mockery::capture` convenience matcher (#1020)
Expand Down
3 changes: 1 addition & 2 deletions composer.json
Expand Up @@ -33,8 +33,7 @@
"require": {
"php": ">=5.6.0",
"lib-pcre": ">=7.0",
"hamcrest/hamcrest-php": "~2.0",
"sebastian/comparator": "^1.2.4|^3.0"
"hamcrest/hamcrest-php": "~2.0"
},
"require-dev": {
"phpunit/phpunit": "~5.7.10|~6.5|~7.0|~8.0"
Expand Down
169 changes: 13 additions & 156 deletions library/Mockery/Exception/NoMatchingExpectationException.php
Expand Up @@ -20,194 +20,51 @@

namespace Mockery\Exception;

use Hamcrest\Util;
use Mockery;
use SebastianBergmann\Comparator\ComparisonFailure;
use SebastianBergmann\Comparator\Factory;

class NoMatchingExpectationException extends Mockery\Exception
{
/**
* @var string
*/
protected $method;
protected $method = null;

/**
* @var array
*/
protected $actual;
protected $actual = array();

/**
* @var Mockery\MockInterface
*/
protected $mockObject;
protected $mockObject = null;

/**
* @param string $methodName
* @param array $actualArguments
* @param array $expectations
*/
public function __construct(
Mockery\MockInterface $mock,
$methodName,
$actualArguments,
$expectations
) {
$this->setMock($mock);
$this->setMethodName($methodName);
$this->setActualArguments($actualArguments);

$diffs = [];
foreach ($expectations as $expectation) {
$expectedArguments = $expectation->getExpectedArgs();

$diff = $this->diff(
$this->normalizeForDiff($expectedArguments),
$this->normalizeForDiff($actualArguments)
);
if (null === $diff) {
// If we reach this, it means that the exception has not been
// raised by a non-strict equality. So the diff is null.
// We do the comparison again but this time comparing references
// of objects.
$diff = $this->diff(
$this->normalizeForStrictDiff($expectedArguments),
$this->normalizeForStrictDiff($actualArguments)
);
}

$diffs[] = sprintf(
"\n%s::%s with arguments%s",
$expectation->getMock()->mockery_getName(),
$expectation->getName(),
null !== $diff ? $diff : "\n### No diff ###"
);
}

$message = 'No matching expectation found for '
. $this->getMockName() . '::'
. \Mockery::formatArgs($methodName, $actualArguments)
. '. Either the method was unexpected or its arguments matched'
. ' no expected argument list for this method.'
. PHP_EOL . PHP_EOL
. 'Here is the list of available expectations and their diff with actual input:'
. PHP_EOL
. implode('', $diffs);

parent::__construct($message, 0, null);
}

/**
* @return $this
*/
private function setMock(Mockery\MockInterface $mock)
public function setMock(Mockery\LegacyMockInterface $mock)
{
$this->mockObject = $mock;
return $this;
}

/**
* @param string $name
*
* @return $this
*/
private function setMethodName($name)
public function setMethodName($name)
{
$this->method = $name;
return $this;
}

/**
* @param array $count
*
* @return $this
*/
private function setActualArguments($count)
public function setActualArguments($count)
{
$this->actual = $count;
return $this;
}

/**
* @return array
*/
public function getActualArguments()
{
return $this->actual;
}

/**
* @return Mockery\MockInterface
*/
private function getMock()
public function getMock()
{
return $this->mockObject;
}

/**
* @return string
*/
private function getMockName()
public function getMethodName()
{
return $this->getMock()->mockery_getName();
}

/**
* @param array $expectedArguments
* @param array $actualArguments
*
* @return string|null
*/
private function diff($expectedArguments, $actualArguments)
{
$comparatorFactory = new Factory();
$comparator = $comparatorFactory->getComparatorFor(
$expectedArguments,
$actualArguments
);
try {
$comparator->assertEquals($expectedArguments, $actualArguments);
} catch (ComparisonFailure $e) {
return $e->getDiff();
}

return null;
return $this->method;
}

/**
* @param array $args
*
* @return array
*/
private function normalizeForDiff($args)
public function getActualArguments()
{
// Wraps items with an IsEqual matcher if it isn't a matcher already
// in order to be sure to compare same nature objects.
return Util::createMatcherArray($args);
return $this->actual;
}

/**
* @param array $args
*
* @return array
*/
private function normalizeForStrictDiff($args)
public function getMockName()
{
$normalized = [];
foreach ($args as $arg) {
if (!is_object($arg)) {
$normalizedArg = Util::createMatcherArray([$arg]);
$normalized[] = reset($normalizedArg);
continue;
}

$objectRef = function_exists('spl_object_id')
? spl_object_id($arg)
: spl_object_hash($arg);

$normalized[] = get_class($arg).'#ref_'.$objectRef;
}

return $normalized;
return $this->getMock()->mockery_getName();
}
}
8 changes: 0 additions & 8 deletions library/Mockery/Expectation.php
Expand Up @@ -911,12 +911,4 @@ public function getExceptionMessage()
{
return $this->_because;
}

/**
* @return array
*/
public function getExpectedArgs()
{
return $this->_expectedArgs;
}
}
17 changes: 12 additions & 5 deletions library/Mockery/ExpectationDirector.php
Expand Up @@ -89,12 +89,19 @@ public function call(array $args)
{
$expectation = $this->findExpectation($args);
if (is_null($expectation)) {
throw new \Mockery\Exception\NoMatchingExpectationException(
$this->_mock,
$this->_name,
$args,
$this->getExpectations()
$exception = new \Mockery\Exception\NoMatchingExpectationException(
'No matching handler found for '
. $this->_mock->mockery_getName() . '::'
. \Mockery::formatArgs($this->_name, $args)
. '. Either the method was unexpected or its arguments matched'
. ' no expected argument list for this method'
. PHP_EOL . PHP_EOL
. \Mockery::formatObjects($args)
);
$exception->setMock($this->_mock)
->setMethodName($this->_name)
->setActualArguments($args);
throw $exception;
}
return $expectation->verifyCall($args);
}
Expand Down
4 changes: 2 additions & 2 deletions tests/Mockery/ContainerTest.php
Expand Up @@ -1239,7 +1239,7 @@ public function testHandlesMethodWithArgumentExpectationWhenCalledWithResource()
$mock->shouldReceive('foo')->with(array('yourself' => 21));

$this->expectException(\Mockery\Exception\NoMatchingExpectationException::class);
$this->expectExceptionMessage("0 => Hamcrest\Core\IsEqual Object (...)");
$this->expectExceptionMessage('MyTestClass::foo(resource(...))');
$mock->foo(fopen('php://memory', 'r'));
}

Expand All @@ -1252,7 +1252,7 @@ public function testHandlesMethodWithArgumentExpectationWhenCalledWithCircularAr
$mock->shouldReceive('foo')->with(array('yourself' => 21));

$this->expectException(\Mockery\Exception\NoMatchingExpectationException::class);
$this->expectExceptionMessage("'myself' => Hamcrest\Core\IsEqual Object (...)");
$this->expectExceptionMessage("MyTestClass::foo(['myself' => [...]])");
$mock->foo($testArray);
}

Expand Down
2 changes: 1 addition & 1 deletion tests/Mockery/ExpectationTest.php
Expand Up @@ -1436,7 +1436,7 @@ public function testClassConstraintThrowsExceptionWhenConstraintUnmatched()
{
$this->mock->shouldReceive('foo')->with(Mockery::type('stdClass'));
$this->expectException(\Mockery\Exception::class);
$this->mock->foo(new \DateTime());
$this->mock->foo(new Exception);
Mockery::close();
}

Expand Down