diff --git a/src/Codeception/Command/DryRun.php b/src/Codeception/Command/DryRun.php index 3760887a7a..7e9987fa07 100644 --- a/src/Codeception/Command/DryRun.php +++ b/src/Codeception/Command/DryRun.php @@ -6,12 +6,22 @@ use Codeception\Event\SuiteEvent; use Codeception\Event\TestEvent; use Codeception\Events; +use Codeception\Lib\Generator\Actions; +use Codeception\Lib\ModuleContainer; +use Codeception\Module; use Codeception\Step; +use Codeception\Stub; use Codeception\Subscriber\Bootstrap as BootstrapLoader; use Codeception\Subscriber\Console as ConsolePrinter; use Codeception\SuiteManager; use Codeception\Test\Interfaces\ScenarioDriven; use Codeception\Test\Test; +use Codeception\Util\Maybe; +use PHPUnit\Framework\MockObject\MockObject; +use ReflectionIntersectionType; +use ReflectionMethod; +use ReflectionType; +use ReflectionUnionType; use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; @@ -74,11 +84,13 @@ public function execute(InputInterface $input, OutputInterface $output) $dispatcher->addSubscriber(new BootstrapLoader()); $suiteManager = new SuiteManager($dispatcher, $suite, $settings); + $moduleContainer = $suiteManager->getModuleContainer(); + foreach (Configuration::modules($settings) as $module) { + $this->mockModule($module, $moduleContainer); + } $suiteManager->loadTests($test); $tests = $suiteManager->getSuite()->tests(); - Step::enableDryRun(); - $this->dispatch($dispatcher, Events::SUITE_INIT, new SuiteEvent($suiteManager->getSuite(), null, $settings)); $this->dispatch($dispatcher, Events::SUITE_BEFORE, new SuiteEvent($suiteManager->getSuite(), null, $settings)); foreach ($tests as $test) { @@ -128,4 +140,111 @@ protected function dryRunTest(OutputInterface $output, EventDispatcher $dispatch } $output->writeln(''); } + + /** + * @return Module&MockObject + */ + private function mockModule($moduleName, ModuleContainer $moduleContainer) + { + $module = $moduleContainer->getModule($moduleName); + $class = new \ReflectionClass($module); + $methodResults = []; + foreach ($class->getMethods(ReflectionMethod::IS_PUBLIC) as $method) { + if ($method->isConstructor()) { + continue; + } + $methodResults[$method->getName()] = $this->getDefaultResultForMethod($class, $method); + } + + $moduleContainer->mock($moduleName, Stub::makeEmpty($module, $methodResults)); + } + + private function getDefaultResultForMethod(\ReflectionClass $class, ReflectionMethod $method) + { + if (PHP_VERSION_ID < 70000) { + return new Maybe(); + } + + $returnType = $method->getReturnType(); + + if ($returnType === null || $returnType->allowsNull()) { + return null; + } + + if ($returnType instanceof ReflectionUnionType) { + return $this->getDefaultValueOfUnionType($returnType); + } + if ($returnType instanceof ReflectionIntersectionType) { + return $this->returnDefaultValueForIntersectionType($returnType); + } + + if (PHP_VERSION_ID >= 70100 && $returnType->isBuiltin()) { + return $this->getDefaultValueForBuiltinType($returnType); + } + + $typeName = Actions::stringifyNamedType($returnType, $class); + return Stub::makeEmpty($typeName); + } + + + + private function getDefaultValueForBuiltinType(ReflectionType $returnType) + { + switch ($returnType->getName()) { + case 'mixed': + case 'void': + return null; + case 'string': + return ''; + case 'int': + return 0; + case 'float': + return 0.0; + case 'bool': + return false; + case 'array': + return []; + case 'resource': + return fopen('data://text/plain;base64,', 'r'); + default: + throw new \Exception('Unsupported return type ' . $returnType->getName()); + } + } + + private function getDefaultValueOfUnionType($returnType) + { + $unionTypes = $returnType->getTypes(); + foreach ($unionTypes as $type) { + if ($type->isBuiltin()) { + return $this->getDefaultValueForBuiltinType($type); + } + } + + return Stub::makeEmpty($unionTypes[0]); + } + + private function returnDefaultValueForIntersectionType(ReflectionIntersectionType $returnType) + { + $extends = null; + $implements = []; + foreach ($returnType->getTypes() as $type) { + if (class_exists($type)) { + $extends = $type; + } else { + $implements [] = $type; + } + } + $className = uniqid('anonymous_class_'); + $code = "abstract class $className"; + if ($extends !== null) { + $code .= " extends \\$extends"; + } + if (count($implements) > 0) { + $code .= ' implements ' . implode(', ', $implements); + } + $code .= ' {}'; + eval($code); + + return Stub::makeEmpty($className); + } } diff --git a/src/Codeception/Step.php b/src/Codeception/Step.php index db2734d018..9b4f08d5bb 100644 --- a/src/Codeception/Step.php +++ b/src/Codeception/Step.php @@ -1,12 +1,10 @@ action = $action; @@ -286,11 +282,7 @@ public function run(ModuleContainer $container = null) } try { - if (self::$dryRun) { - return $this->getDefaultResultForMethod($activeModule, $this->action); - } - - return call_user_func_array([$activeModule, $this->action], $this->arguments); + $res = call_user_func_array([$activeModule, $this->action], $this->arguments); } catch (\Exception $e) { if ($this->isTry) { throw $e; @@ -301,6 +293,7 @@ public function run(ModuleContainer $container = null) } throw $e; } + return $res; } @@ -365,111 +358,4 @@ public function getPrefix() { return $this->prefix . ' '; } - - public static function enableDryRun() - { - self::$dryRun = true; - } - - private function getDefaultResultForMethod(Module $module, $method) - { - if (PHP_VERSION_ID < 70000) { - return new Maybe(); - } - - $reflection = new \ReflectionMethod($module, $method); - $returnType = $reflection->getReturnType(); - - if ($returnType === null || $returnType->allowsNull()) { - return null; - } - - if ($returnType instanceof \ReflectionUnionType) { - return $this->getDefaultValueOfUnionType($returnType); - } - if ($returnType instanceof \ReflectionIntersectionType) { - return $this->returnDefaultValueForIntersectionType($returnType); - } - - if (PHP_VERSION_ID >= 70100 && $returnType->isBuiltin()) { - return $this->getDefaultValueForBuiltinType($returnType); - } - - $typeName = Actions::stringifyNamedType($returnType, new \ReflectionClass($module)); - return Stub::makeEmpty($typeName); - } - - /** - * @param \ReflectionType $returnType - * @return array|false|float|int|resource|string|null - * @throws \Exception - */ - private function getDefaultValueForBuiltinType(\ReflectionType $returnType) - { - switch ($returnType->getName()) { - case 'mixed': - case 'void': - return null; - case 'string': - return ''; - case 'int': - return 0; - case 'float': - return 0.0; - case 'bool': - return false; - case 'array': - return []; - case 'resource': - return fopen('data://text/plain;base64,', 'r'); - default: - throw new \Exception('Unsupported return type ' . $returnType->getName()); - } - } - - /** - * @param $returnType - * @return array|callable|false|float|int|mixed|object|MockObject|resource|string|null - * @throws \Exception - */ - private function getDefaultValueOfUnionType($returnType) - { - $unionTypes = $returnType->getTypes(); - foreach ($unionTypes as $type) { - if ($type->isBuiltin()) { - return $this->getDefaultValueForBuiltinType($type); - } - } - - return Stub::makeEmpty($unionTypes[0]); - } - - /** - * @param \ReflectionIntersectionType $returnType - * @return \#g#F\Codeception\uniqid|\#π(#g#F\Codeception\uniqid)("anonymous_class_")|mixed|object|MockObject|string - * @throws \ReflectionException - */ - private function returnDefaultValueForIntersectionType(\ReflectionIntersectionType $returnType) - { - $extends = null; - $implements = []; - foreach ($returnType->getTypes() as $type) { - if (class_exists($type)) { - $extends = $type; - } else { - $implements [] = $type; - } - } - $className = uniqid('anonymous_class_'); - $code = "abstract class $className"; - if ($extends !== null) { - $code .= " extends \\$extends"; - } - if (count($implements) > 0) { - $code .= ' implements ' . implode(', ', $implements); - } - $code .= ' {}'; - eval($code); - return Stub::constructEmpty($className); - } }