public function build(\reflectionClass $class, &$instance = null) { $this->factory = null; if ($class->isInterface() === false && $class->isAbstract() === false) { $constructor = $class->getConstructor(); if ($constructor === null || $constructor->isPublic() === true) { $constructorParameters = $closureParameters = array(); if ($constructor !== null) { $this->allArgumentsAreOptional = $constructor->getNumberOfRequiredParameters() === 0; foreach ($constructor->getParameters() as $position => $parameter) { $closureParameters[$position] = ($parameter->isPassedByReference() === false ? '' : '& ') . ($constructorParameters[$position] = '$' . $parameter->getName()); if (self::isVariadic($parameter)) { $closureParameters[$position] = '...' . $closureParameters[$position]; $constructorParameters[$position] = '...' . $constructorParameters[$position]; } switch (true) { case $parameter->isDefaultValueAvailable(): $defaultValue = var_export($parameter->getDefaultValue(), true); break; case $parameter->isOptional() && self::isVariadic($parameter) === false: $defaultValue = 'null'; break; default: $defaultValue = null; } if ($defaultValue !== null) { $closureParameters[$position] .= ' = ' . $defaultValue; } } } if ($constructor === null || sizeof($closureParameters) <= 0) { $this->factory = function () use(&$instance, $class) { return $instance = $class->newInstanceArgs(func_get_args()); }; } else { $this->factory = eval('return function(' . join(', ', $closureParameters) . ') use (& $instance) { return ($instance = new ' . $class->getName() . '(' . join(', ', $constructorParameters) . ')); };'); } } } return $this; }
public function runTestMethod($testMethod, array $tags = array()) { if ($this->methodIsIgnored($testMethod, $tags) === false) { $this->mockAutoloader->setMockGenerator($this->mockGenerator)->register(); set_error_handler(array($this, 'errorHandler')); ini_set('display_errors', 'stderr'); ini_set('log_errors', 'Off'); ini_set('log_errors_max_len', '0'); $this->currentMethod = $testMethod; $this->executeOnFailure = array(); $this->phpMocker->setDefaultNamespace($this->getTestedClassNamespace()); try { foreach ($this->getMethodPhpVersions($testMethod) as $phpVersion => $operator) { if (version_compare(phpversion(), $phpVersion, $operator) === false) { throw new test\exceptions\skip('PHP version ' . PHP_VERSION . ' is not ' . $operator . ' to ' . $phpVersion); } } foreach ($this->getMandatoryMethodExtensions($testMethod) as $mandatoryExtension) { try { call_user_func($this->phpExtensionFactory, $mandatoryExtension)->requireExtension(); } catch (atoum\php\exception $exception) { throw new test\exceptions\skip($exception->getMessage()); } } try { ob_start(); test\adapter::setStorage($this->testAdapterStorage); mock\controller::setLinker($this->mockControllerLinker); $this->testAdapterStorage->add(php\mocker::getAdapter()); $this->beforeTestMethod($this->currentMethod); $this->mockGenerator->testedClassIs($this->getTestedClassName()); try { $testedClass = new \reflectionClass($testedClassName = $this->getTestedClassName()); } catch (\exception $exception) { throw new exceptions\runtime('Tested class \'' . $testedClassName . '\' does not exist for test class \'' . $this->getClass() . '\''); } if ($testedClass->isAbstract() === true) { $testedClass = new \reflectionClass($testedClassName = $this->mockGenerator->getDefaultNamespace() . '\\' . $testedClassName); } $this->factoryBuilder->build($testedClass, $instance)->addToAssertionManager($this->assertionManager, 'newTestedInstance', function () use($testedClass) { throw new exceptions\runtime('Tested class ' . $testedClass->getName() . ' has no constructor or its constructor has at least one mandatory argument'); }); $this->factoryBuilder->build($testedClass)->addToAssertionManager($this->assertionManager, 'newInstance', function () use($testedClass) { throw new exceptions\runtime('Tested class ' . $testedClass->getName() . ' has no constructor or its constructor has at least one mandatory argument'); }); $this->assertionManager->setPropertyHandler('testedInstance', function () use(&$instance) { if ($instance === null) { throw new exceptions\runtime('Use $this->newTestedInstance before using $this->testedInstance'); } return $instance; }); if ($this->codeCoverageIsEnabled() === true) { $options = XDEBUG_CC_UNUSED | XDEBUG_CC_DEAD_CODE; if ($this->branchesAndPathsCoverageIsEnabled() === true) { $options |= XDEBUG_CC_BRANCH_CHECK; } xdebug_start_code_coverage($options); } $assertionNumber = $this->score->getAssertionNumber(); $time = microtime(true); $memory = memory_get_usage(true); if (isset($this->dataProviders[$testMethod]) === false) { $this->{$testMethod}(); $this->asserterCallManager->check(); } else { $data = $this->{$this->dataProviders[$testMethod]}(); if (is_array($data) === false && $data instanceof \traversable === false) { throw new test\exceptions\runtime('Data provider ' . $this->getClass() . '::' . $this->dataProviders[$testMethod] . '() must return an array or an iterator'); } $reflectedTestMethod = call_user_func($this->reflectionMethodFactory, $this, $testMethod); $numberOfArguments = $reflectedTestMethod->getNumberOfRequiredParameters(); foreach ($data as $key => $arguments) { if (is_array($arguments) === false) { $arguments = array($arguments); } if (sizeof($arguments) != $numberOfArguments) { throw new test\exceptions\runtime('Data provider ' . $this->getClass() . '::' . $this->dataProviders[$testMethod] . '() not provide enough arguments at key ' . $key . ' for test method ' . $this->getClass() . '::' . $testMethod . '()'); } $this->score->setDataSet($key, $this->dataProviders[$testMethod]); $reflectedTestMethod->invokeArgs($this, $arguments); $this->asserterCallManager->check(); $this->score->unsetDataSet(); } } $this->mockControllerLinker->reset(); $this->testAdapterStorage->reset(); $memoryUsage = memory_get_usage(true) - $memory; $duration = microtime(true) - $time; $this->score->addMemoryUsage($this->path, $this->class, $this->currentMethod, $memoryUsage)->addDuration($this->path, $this->class, $this->currentMethod, $duration)->addOutput($this->path, $this->class, $this->currentMethod, ob_get_clean()); if ($this->codeCoverageIsEnabled() === true) { $this->score->getCoverage()->addXdebugDataForTest($this, xdebug_get_code_coverage()); xdebug_stop_code_coverage(); } if ($assertionNumber == $this->score->getAssertionNumber() && $this->methodIsNotVoid($this->currentMethod) === false) { $this->score->addVoidMethod($this->path, $this->class, $this->currentMethod); } } catch (\exception $exception) { $this->score->addOutput($this->path, $this->class, $this->currentMethod, ob_get_clean()); throw $exception; } } catch (asserter\exception $exception) { foreach ($this->executeOnFailure as $closure) { ob_start(); $closure(); $this->score->addOutput($this->path, $this->class, $this->currentMethod, ob_get_clean()); } if ($this->score->failExists($exception) === false) { $this->addExceptionToScore($exception); } } catch (test\exceptions\runtime $exception) { $this->score->addRuntimeException($this->path, $this->class, $this->currentMethod, $exception); } catch (test\exceptions\skip $exception) { list($file, $line) = $this->getBacktrace($exception->getTrace()); $this->score->addSkippedMethod($file, $this->class, $this->currentMethod, $line, $exception->getMessage()); } catch (test\exceptions\stop $exception) { } catch (exception $exception) { list($file, $line) = $this->getBacktrace($exception->getTrace()); $this->errorHandler(E_USER_ERROR, $exception->getMessage(), $file, $line); } catch (\exception $exception) { $this->addExceptionToScore($exception); } $this->afterTestMethod($this->currentMethod); $this->currentMethod = null; restore_error_handler(); ini_restore('display_errors'); ini_restore('log_errors'); ini_restore('log_errors_max_len'); $this->mockAutoloader->unregister(); } return $this; }
protected function generateClassMethodCode(\reflectionClass $class) { $mockedMethods = ''; $mockedMethodNames = array(); $className = $class->getName(); if ($this->allIsInterface && strtolower($className) != $this->testedClass) { foreach ($class->getMethods() as $method) { if ($this->methodIsMockable($method) === true) { $this->orphanize($method->getName()); } } } $constructor = $class->getConstructor(); if ($constructor === null) { $mockedMethods .= self::generateDefaultConstructor(); $mockedMethodNames[] = '__construct'; } else { if ($constructor->isFinal() === false) { $constructorName = $constructor->getName(); $overload = $this->getOverload($constructorName); if ($constructor->isPublic() === false) { $this->shuntParentClassCalls(); if ($overload === null) { $this->overload(new php\method('__construct')); $overload = $this->getOverload('__construct'); } } $parameters = $this->getParameters($constructor); if ($overload === null) { $mockedMethods .= "\t" . 'public function __construct(' . $this->getParametersSignature($constructor) . ')'; } else { $overload->addArgument(php\method\argument::get('mockController')->isObject('\\' . __NAMESPACE__ . '\\controller')->setDefaultValue(null)); $mockedMethods .= "\t" . $overload; } $mockedMethods .= PHP_EOL; $mockedMethods .= "\t" . '{' . PHP_EOL; if (self::hasVariadic($constructor) === true) { $mockedMethods .= "\t\t" . '$arguments = func_get_args();' . PHP_EOL; $mockedMethods .= "\t\t" . '$mockController = \\mageekguy\\atoum\\mock\\controller::get();' . PHP_EOL; } else { $mockedMethods .= "\t\t" . '$arguments = array_merge(array(' . join(', ', $parameters) . '), array_slice(func_get_args(), ' . sizeof($parameters) . ', -1));' . PHP_EOL; $mockedMethods .= "\t\t" . 'if ($mockController === null)' . PHP_EOL; $mockedMethods .= "\t\t" . '{' . PHP_EOL; $mockedMethods .= "\t\t\t" . '$mockController = \\mageekguy\\atoum\\mock\\controller::get();' . PHP_EOL; $mockedMethods .= "\t\t" . '}' . PHP_EOL; } $mockedMethods .= "\t\t" . 'if ($mockController !== null)' . PHP_EOL; $mockedMethods .= "\t\t" . '{' . PHP_EOL; $mockedMethods .= "\t\t\t" . '$this->setMockController($mockController);' . PHP_EOL; $mockedMethods .= "\t\t" . '}' . PHP_EOL; if ($constructor->isAbstract() === true || $this->isShunted('__construct') === true || $this->isShunted($className) === true) { $methodName = $this->isShunted($className) === true ? $className : '__construct'; $mockedMethods .= "\t\t" . 'if (isset($this->getMockController()->' . $methodName . ') === false)' . PHP_EOL; $mockedMethods .= "\t\t" . '{' . PHP_EOL; $mockedMethods .= "\t\t\t" . '$this->getMockController()->' . $methodName . ' = function() {};' . PHP_EOL; $mockedMethods .= "\t\t" . '}' . PHP_EOL; $mockedMethods .= "\t\t" . '$this->getMockController()->invoke(\'' . $methodName . '\', $arguments);' . PHP_EOL; } else { $mockedMethods .= "\t\t" . 'if (isset($this->getMockController()->' . $constructorName . ') === true)' . PHP_EOL; $mockedMethods .= "\t\t" . '{' . PHP_EOL; $mockedMethods .= "\t\t\t" . '$this->getMockController()->invoke(\'' . $constructorName . '\', $arguments);' . PHP_EOL; $mockedMethods .= "\t\t" . '}' . PHP_EOL; $mockedMethods .= "\t\t" . 'else' . PHP_EOL; $mockedMethods .= "\t\t" . '{' . PHP_EOL; $mockedMethods .= "\t\t\t" . '$this->getMockController()->addCall(\'' . $constructorName . '\', $arguments);' . PHP_EOL; if ($this->shuntParentClassCalls === false) { $mockedMethods .= "\t\t\t" . 'call_user_func_array(\'parent::' . $constructorName . '\', $arguments);' . PHP_EOL; } $mockedMethods .= "\t\t" . '}' . PHP_EOL; } $mockedMethods .= "\t" . '}' . PHP_EOL; $mockedMethodNames[] = strtolower($constructorName); } } foreach ($class->getMethods() as $method) { if ($method->isConstructor() === false && $this->methodIsMockable($method) === true) { $methodName = $method->getName(); $mockedMethodNames[] = strtolower($methodName); $overload = $this->getOverload($methodName); $parameters = $this->getParameters($method); if ($overload !== null) { $mockedMethods .= "\t" . $overload; } else { $mockedMethods .= "\t" . ($method->isPublic() === true ? 'public' : 'protected') . ' function' . ($method->returnsReference() === false ? '' : ' &') . ' ' . $methodName . '(' . $this->getParametersSignature($method) . ')' . $this->getReturnType($method); } $mockedMethods .= PHP_EOL . "\t" . '{' . PHP_EOL; if (self::hasVariadic($method) === true) { $mockedMethods .= "\t\t" . '$arguments = func_get_args();' . PHP_EOL; } else { $mockedMethods .= "\t\t" . '$arguments = array_merge(array(' . join(', ', $parameters) . '), array_slice(func_get_args(), ' . sizeof($parameters) . '));' . PHP_EOL; } if ($this->isShunted($methodName) === true || $method->isAbstract() === true) { $mockedMethods .= "\t\t" . 'if (isset($this->getMockController()->' . $methodName . ') === false)' . PHP_EOL; $mockedMethods .= "\t\t" . '{' . PHP_EOL; $mockedMethods .= "\t\t\t" . '$this->getMockController()->' . $methodName . ' = function() {};' . PHP_EOL; $mockedMethods .= "\t\t" . '}' . PHP_EOL; $mockedMethods .= "\t\t" . '$return = $this->getMockController()->invoke(\'' . $methodName . '\', $arguments);' . PHP_EOL; $mockedMethods .= "\t\t" . 'return $return;' . PHP_EOL; } else { $mockedMethods .= "\t\t" . 'if (isset($this->getMockController()->' . $methodName . ') === true)' . PHP_EOL; $mockedMethods .= "\t\t" . '{' . PHP_EOL; $mockedMethods .= "\t\t\t" . '$return = $this->getMockController()->invoke(\'' . $methodName . '\', $arguments);' . PHP_EOL; $mockedMethods .= "\t\t\t" . 'return $return;' . PHP_EOL; $mockedMethods .= "\t\t" . '}' . PHP_EOL; $mockedMethods .= "\t\t" . 'else' . PHP_EOL; $mockedMethods .= "\t\t" . '{' . PHP_EOL; if ($methodName === '__call') { $mockedMethods .= "\t\t\t" . '$this->getMockController()->addCall(current(array_slice($arguments, 0, 1)), current(array_slice($arguments, 1)));' . PHP_EOL; } $mockedMethods .= "\t\t\t" . '$this->getMockController()->addCall(\'' . $methodName . '\', $arguments);' . PHP_EOL; if ($this->shuntParentClassCalls === false) { $mockedMethods .= "\t\t\t" . '$return = call_user_func_array(\'parent::' . $methodName . '\', $arguments);' . PHP_EOL; $mockedMethods .= "\t\t\t" . 'return $return;' . PHP_EOL; } $mockedMethods .= "\t\t" . '}' . PHP_EOL; } $mockedMethods .= "\t" . '}' . PHP_EOL; } } if ($class->isAbstract() && $this->allowUndefinedMethodsUsage === true && in_array('__call', $mockedMethodNames) === false) { $mockedMethods .= self::generate__call(); $mockedMethodNames[] = '__call'; } return $mockedMethods . self::generateGetMockedMethod($mockedMethodNames); }