diff --git a/spec/Prophecy/Call/CallCenterSpec.php b/spec/Prophecy/Call/CallCenterSpec.php index 83d61f1ec..e0cdab646 100644 --- a/spec/Prophecy/Call/CallCenterSpec.php +++ b/spec/Prophecy/Call/CallCenterSpec.php @@ -3,6 +3,7 @@ namespace spec\Prophecy\Call; use PhpSpec\ObjectBehavior; +use Prophecy\Exception\Call\UnexpectedCallException; use Prophecy\Promise\PromiseInterface; use Prophecy\Prophecy\MethodProphecy; use Prophecy\Prophecy\ObjectProphecy; @@ -123,23 +124,6 @@ function it_executes_promise_of_method_prophecy_that_matches_with_highest_score_ ->shouldReturn('second'); } - function it_throws_exception_if_call_does_not_match_any_of_defined_method_prophecies( - $objectProphecy, - MethodProphecy $method, - ArgumentsWildcard $arguments - ) { - $method->getMethodName()->willReturn('getName'); - $method->getArgumentsWildcard()->willReturn($arguments); - $arguments->scoreArguments(array('world', 'everything'))->willReturn(false); - $arguments->__toString()->willReturn('arg1, arg2'); - - $objectProphecy->getMethodProphecies()->willReturn(array('method1' => array($method))); - $objectProphecy->getMethodProphecies('getName')->willReturn(array($method)); - - $this->shouldThrow('Prophecy\Exception\Call\UnexpectedCallException') - ->duringMakeCall($objectProphecy, 'getName', array('world', 'everything')); - } - function it_returns_null_if_method_prophecy_that_matches_makeCall_arguments_has_no_promise( $objectProphecy, MethodProphecy $method, @@ -177,4 +161,25 @@ function it_finds_recorded_calls_by_a_method_name_and_arguments_wildcard( $calls[0]->getMethodName()->shouldReturn('getName'); $calls[0]->getArguments()->shouldReturn(array('everything')); } + + function it_records_the_error_when_stub_has_got_unexpected_method_calls( + $objectProphecy, + MethodProphecy $method, + ArgumentsWildcard $arguments + ) { + $method->getMethodName()->willReturn('getName'); + $method->getArgumentsWildcard()->willReturn($arguments); + + $arguments->getTokens()->willReturn(array()); + + $objectProphecy->getMethodProphecies()->willReturn(array('getName' => array($method))); + $objectProphecy->getMethodProphecies('getName')->willReturn(array($method)); + $objectProphecy->getMethodProphecies('method1')->willReturn(array()); + $objectProphecy->reveal()->willReturn(new \stdClass()); + + $this->shouldNotThrow('Prophecy\Exception\Call\UnexpectedCallException') + ->duringMakeCall($objectProphecy, 'method1', array()); + + $this->shouldThrow('Prophecy\Exception\Call\UnexpectedCallException')->duringCheckUnexpectedCalls(); + } } diff --git a/spec/Prophecy/Prophecy/MethodProphecySpec.php b/spec/Prophecy/Prophecy/MethodProphecySpec.php index 4fddb4738..321d26f37 100644 --- a/spec/Prophecy/Prophecy/MethodProphecySpec.php +++ b/spec/Prophecy/Prophecy/MethodProphecySpec.php @@ -330,7 +330,7 @@ function it_records_even_failed_checked_predictions( $this->withArguments($arguments); try { - $this->callOnWrappedObject('shouldHave', array($prediction)); + $this->callOnWrappedObject('shouldHave', array($prediction)); } catch (\Exception $e) {} $this->getCheckedPredictions()->shouldReturn(array($prediction)); @@ -342,6 +342,7 @@ function it_checks_prediction_via_shouldHave_method_call_with_callback( Call $call1, Call $call2 ) { + $objectProphecy->addMethodProphecy($this)->willReturn(null); $callback = function ($calls, $object, $method) { throw new RuntimeException; }; diff --git a/spec/Prophecy/Prophecy/ObjectProphecySpec.php b/spec/Prophecy/Prophecy/ObjectProphecySpec.php index c6afb3ef0..7221257f0 100644 --- a/spec/Prophecy/Prophecy/ObjectProphecySpec.php +++ b/spec/Prophecy/Prophecy/ObjectProphecySpec.php @@ -264,6 +264,17 @@ function it_returns_new_MethodProphecy_for_all_callback_signatures( $methodProphecy2->shouldNotBe($methodProphecy1); } + + function it_throws_UnexpectedCallException_during_checkPredictions_if_unexpected_method_was_called( + $lazyDouble, CallCenter $callCenter + ) { + $this->beConstructedWith($lazyDouble, $callCenter); + + $callCenter->checkUnexpectedCalls()->willThrow('Prophecy\Exception\Call\UnexpectedCallException'); + + $this->shouldThrow('Prophecy\Exception\Call\UnexpectedCallException') + ->duringCheckProphecyMethodsPredictions(); + } } class ObjectProphecySpecFixtureA diff --git a/src/Prophecy/Call/CallCenter.php b/src/Prophecy/Call/CallCenter.php index bc936c8f1..f61b68b6f 100644 --- a/src/Prophecy/Call/CallCenter.php +++ b/src/Prophecy/Call/CallCenter.php @@ -17,6 +17,7 @@ use Prophecy\Argument\ArgumentsWildcard; use Prophecy\Util\StringUtil; use Prophecy\Exception\Call\UnexpectedCallException; +use SplObjectStorage; /** * Calls receiver & manager. @@ -32,6 +33,11 @@ class CallCenter */ private $recordedCalls = array(); + /** + * @var SplObjectStorage + */ + private $unexpectedCalls; + /** * Initializes call center. * @@ -40,6 +46,7 @@ class CallCenter public function __construct(StringUtil $util = null) { $this->util = $util ?: new StringUtil; + $this->unexpectedCalls = new SplObjectStorage(); } /** @@ -81,16 +88,14 @@ public function makeCall(ObjectProphecy $prophecy, $methodName, array $arguments } // There are method prophecies, so it's a fake/stub. Searching prophecy for this call - $matches = array(); - foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) { - if (0 < $score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments)) { - $matches[] = array($score, $methodProphecy); - } - } + $matches = $this->findMethodProphecies($prophecy, $methodName, $arguments); // If fake/stub doesn't have method prophecy for this call - throw exception if (!count($matches)) { - throw $this->createUnexpectedCallException($prophecy, $methodName, $arguments); + $this->unexpectedCalls->attach(new Call($methodName, $arguments, null, null, $file, $line), $prophecy); + $this->recordedCalls[] = new Call($methodName, $arguments, null, null, $file, $line); + + return null; } // Sort matches by their score value @@ -147,6 +152,22 @@ public function findCalls($methodName, ArgumentsWildcard $wildcard) ); } + /** + * @throws UnexpectedCallException + */ + public function checkUnexpectedCalls() + { + /** @var Call $call */ + foreach ($this->unexpectedCalls as $call) { + $prophecy = $this->unexpectedCalls[$call]; + + // If fake/stub doesn't have method prophecy for this call - throw exception + if (!count($this->findMethodProphecies($prophecy, $call->getMethodName(), $call->getArguments()))) { + throw $this->createUnexpectedCallException($prophecy, $call->getMethodName(), $call->getArguments()); + } + } + } + private function createUnexpectedCallException(ObjectProphecy $prophecy, $methodName, array $arguments) { @@ -226,4 +247,23 @@ function () use ($indentationLength) { $arguments ); } + + /** + * @param ObjectProphecy $prophecy + * @param string $methodName + * @param array $arguments + * + * @return array + */ + private function findMethodProphecies(ObjectProphecy $prophecy, $methodName, array $arguments) + { + $matches = array(); + foreach ($prophecy->getMethodProphecies($methodName) as $methodProphecy) { + if (0 < $score = $methodProphecy->getArgumentsWildcard()->scoreArguments($arguments)) { + $matches[] = array($score, $methodProphecy); + } + } + + return $matches; + } } diff --git a/src/Prophecy/Prophecy/ObjectProphecy.php b/src/Prophecy/Prophecy/ObjectProphecy.php index 8d8f8a1bb..1c6626b75 100644 --- a/src/Prophecy/Prophecy/ObjectProphecy.php +++ b/src/Prophecy/Prophecy/ObjectProphecy.php @@ -208,12 +208,15 @@ public function findProphecyMethodCalls($methodName, ArgumentsWildcard $wildcard * Checks that registered method predictions do not fail. * * @throws \Prophecy\Exception\Prediction\AggregateException If any of registered predictions fail + * @throws \Prophecy\Exception\Call\UnexpectedCallException */ public function checkProphecyMethodsPredictions() { $exception = new AggregateException(sprintf("%s:\n", get_class($this->reveal()))); $exception->setObjectProphecy($this); + $this->callCenter->checkUnexpectedCalls(); + foreach ($this->methodProphecies as $prophecies) { foreach ($prophecies as $prophecy) { try {