Chris@14: Chris@14: * Chris@14: * For the full copyright and license information, please view the LICENSE Chris@14: * file that was distributed with this source code. Chris@14: */ Chris@14: namespace PHPUnit\Framework\MockObject\Builder; Chris@14: Chris@14: use PHPUnit\Framework\Constraint\Constraint; Chris@14: use PHPUnit\Framework\MockObject\Matcher; Chris@14: use PHPUnit\Framework\MockObject\Matcher\Invocation; Chris@14: use PHPUnit\Framework\MockObject\RuntimeException; Chris@14: use PHPUnit\Framework\MockObject\Stub; Chris@14: use PHPUnit\Framework\MockObject\Stub\MatcherCollection; Chris@14: Chris@14: /** Chris@14: * Builder for mocked or stubbed invocations. Chris@14: * Chris@14: * Provides methods for building expectations without having to resort to Chris@14: * instantiating the various matchers manually. These methods also form a Chris@14: * more natural way of reading the expectation. This class should be together Chris@14: * with the test case PHPUnit\Framework\MockObject\TestCase. Chris@14: */ Chris@14: class InvocationMocker implements MethodNameMatch Chris@14: { Chris@14: /** Chris@14: * @var MatcherCollection Chris@14: */ Chris@14: private $collection; Chris@14: Chris@14: /** Chris@14: * @var Matcher Chris@14: */ Chris@14: private $matcher; Chris@14: Chris@14: /** Chris@14: * @var string[] Chris@14: */ Chris@14: private $configurableMethods = []; Chris@14: Chris@14: /** Chris@14: * @param MatcherCollection $collection Chris@14: * @param Invocation $invocationMatcher Chris@14: * @param array $configurableMethods Chris@14: */ Chris@14: public function __construct(MatcherCollection $collection, Invocation $invocationMatcher, array $configurableMethods) Chris@14: { Chris@14: $this->collection = $collection; Chris@14: $this->matcher = new Matcher($invocationMatcher); Chris@14: Chris@14: $this->collection->addMatcher($this->matcher); Chris@14: Chris@14: $this->configurableMethods = $configurableMethods; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @return Matcher Chris@14: */ Chris@14: public function getMatcher() Chris@14: { Chris@14: return $this->matcher; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param mixed $id Chris@14: * Chris@14: * @return InvocationMocker Chris@14: */ Chris@14: public function id($id) Chris@14: { Chris@14: $this->collection->registerId($id, $this); Chris@14: Chris@14: return $this; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param Stub $stub Chris@14: * Chris@14: * @return InvocationMocker Chris@14: */ Chris@14: public function will(Stub $stub) Chris@14: { Chris@14: $this->matcher->setStub($stub); Chris@14: Chris@14: return $this; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param mixed $value Chris@14: * @param mixed $nextValues, ... Chris@14: * Chris@14: * @return InvocationMocker Chris@14: */ Chris@14: public function willReturn($value, ...$nextValues) Chris@14: { Chris@14: if (\count($nextValues) === 0) { Chris@14: $stub = new Stub\ReturnStub($value); Chris@14: } else { Chris@14: $stub = new Stub\ConsecutiveCalls( Chris@14: \array_merge([$value], $nextValues) Chris@14: ); Chris@14: } Chris@14: Chris@14: return $this->will($stub); Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param mixed $reference Chris@14: * Chris@14: * @return InvocationMocker Chris@14: */ Chris@14: public function willReturnReference(&$reference) Chris@14: { Chris@14: $stub = new Stub\ReturnReference($reference); Chris@14: Chris@14: return $this->will($stub); Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param array $valueMap Chris@14: * Chris@14: * @return InvocationMocker Chris@14: */ Chris@14: public function willReturnMap(array $valueMap) Chris@14: { Chris@14: $stub = new Stub\ReturnValueMap($valueMap); Chris@14: Chris@14: return $this->will($stub); Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param mixed $argumentIndex Chris@14: * Chris@14: * @return InvocationMocker Chris@14: */ Chris@14: public function willReturnArgument($argumentIndex) Chris@14: { Chris@14: $stub = new Stub\ReturnArgument($argumentIndex); Chris@14: Chris@14: return $this->will($stub); Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param callable $callback Chris@14: * Chris@14: * @return InvocationMocker Chris@14: */ Chris@14: public function willReturnCallback($callback) Chris@14: { Chris@14: $stub = new Stub\ReturnCallback($callback); Chris@14: Chris@14: return $this->will($stub); Chris@14: } Chris@14: Chris@14: /** Chris@14: * @return InvocationMocker Chris@14: */ Chris@14: public function willReturnSelf() Chris@14: { Chris@14: $stub = new Stub\ReturnSelf; Chris@14: Chris@14: return $this->will($stub); Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param mixed $values, ... Chris@14: * Chris@14: * @return InvocationMocker Chris@14: */ Chris@14: public function willReturnOnConsecutiveCalls(...$values) Chris@14: { Chris@14: $stub = new Stub\ConsecutiveCalls($values); Chris@14: Chris@14: return $this->will($stub); Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param \Exception $exception Chris@14: * Chris@14: * @return InvocationMocker Chris@14: */ Chris@14: public function willThrowException(\Exception $exception) Chris@14: { Chris@14: $stub = new Stub\Exception($exception); Chris@14: Chris@14: return $this->will($stub); Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param mixed $id Chris@14: * Chris@14: * @return InvocationMocker Chris@14: */ Chris@14: public function after($id) Chris@14: { Chris@14: $this->matcher->setAfterMatchBuilderId($id); Chris@14: Chris@14: return $this; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param array ...$arguments Chris@14: * Chris@14: * @return InvocationMocker Chris@14: * Chris@14: * @throws RuntimeException Chris@14: */ Chris@14: public function with(...$arguments) Chris@14: { Chris@14: $this->canDefineParameters(); Chris@14: Chris@14: $this->matcher->setParametersMatcher(new Matcher\Parameters($arguments)); Chris@14: Chris@14: return $this; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param array ...$arguments Chris@14: * Chris@14: * @return InvocationMocker Chris@14: * Chris@14: * @throws RuntimeException Chris@14: */ Chris@14: public function withConsecutive(...$arguments) Chris@14: { Chris@14: $this->canDefineParameters(); Chris@14: Chris@14: $this->matcher->setParametersMatcher(new Matcher\ConsecutiveParameters($arguments)); Chris@14: Chris@14: return $this; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @return InvocationMocker Chris@14: * Chris@14: * @throws RuntimeException Chris@14: */ Chris@14: public function withAnyParameters() Chris@14: { Chris@14: $this->canDefineParameters(); Chris@14: Chris@14: $this->matcher->setParametersMatcher(new Matcher\AnyParameters); Chris@14: Chris@14: return $this; Chris@14: } Chris@14: Chris@14: /** Chris@14: * @param Constraint|string $constraint Chris@14: * Chris@14: * @return InvocationMocker Chris@14: * Chris@14: * @throws RuntimeException Chris@14: */ Chris@14: public function method($constraint) Chris@14: { Chris@14: if ($this->matcher->hasMethodNameMatcher()) { Chris@14: throw new RuntimeException( Chris@14: 'Method name matcher is already defined, cannot redefine' Chris@14: ); Chris@14: } Chris@14: Chris@14: if (\is_string($constraint) && !\in_array(\strtolower($constraint), $this->configurableMethods)) { Chris@14: throw new RuntimeException( Chris@14: \sprintf( Chris@14: 'Trying to configure method "%s" which cannot be configured because it does not exist, has not been specified, is final, or is static', Chris@14: $constraint Chris@14: ) Chris@14: ); Chris@14: } Chris@14: Chris@14: $this->matcher->setMethodNameMatcher(new Matcher\MethodName($constraint)); Chris@14: Chris@14: return $this; Chris@14: } Chris@14: Chris@14: /** Chris@14: * Validate that a parameters matcher can be defined, throw exceptions otherwise. Chris@14: * Chris@14: * @throws RuntimeException Chris@14: */ Chris@14: private function canDefineParameters() Chris@14: { Chris@14: if (!$this->matcher->hasMethodNameMatcher()) { Chris@14: throw new RuntimeException( Chris@14: 'Method name matcher is not defined, cannot define parameter ' . Chris@14: 'matcher without one' Chris@14: ); Chris@14: } Chris@14: Chris@14: if ($this->matcher->hasParametersMatcher()) { Chris@14: throw new RuntimeException( Chris@14: 'Parameter matcher is already defined, cannot redefine' Chris@14: ); Chris@14: } Chris@14: } Chris@14: }