diff --git a/docs/07-AdvancedUsage.md b/docs/07-AdvancedUsage.md
index 8daa6825b8..6b44e6ae86 100644
--- a/docs/07-AdvancedUsage.md
+++ b/docs/07-AdvancedUsage.md
@@ -65,6 +65,86 @@ As you see, Cest class have no parents like `\Codeception\TestCase\Test` or `PHP
Also you can define `_failed` method in Cest class which will be called if test finishes with `error` or fails.
+## Dependency Injection
+
+Codeception supports simple dependency injection for Cest and \Codeception\TestCase\Test classes. It means that you can specify which classes you need as parameters of special `_inject()` method, and Codeception will automatically create respective objects and invoke this method, passing all dependencies as arguments. This can be useful when working with Helpers, for example:
+
+```php
+signUp = $signUp;
+ $this->navBar = $navBar;
+ }
+
+ public function signUp(AcceptanceTester $I)
+ {
+ $I->wantTo('sign up');
+
+ $this->navBar->click('Sign up');
+ $this->signUp->register([
+ 'first_name' => 'Joe',
+ 'last_name' => 'Jones',
+ 'email' => 'joe@jones.com',
+ 'password' => '1234',
+ 'password_confirmation' => '1234'
+ ]);
+ }
+}
+?>
+```
+
+Just make sure that all Helpers can be autoloaded.
+
+Example of Test class:
+
+```
+math = $math;
+ }
+
+ public function testAll()
+ {
+ $this->assertEquals(3, $this->math->add(1, 2));
+ $this->assertEquals(1, $this->math->subtract(3, 2));
+ }
+}
+?>
+```
+
+It is usually preferable to make `_inject()` method `protected` or `private` for not to confuse it with `public` test methods. `_inject()` will be invoked just once right after creation of TestCase object (either Cest or Test).
+
+Moreover, Codeception can resolve dependencies recursively (when `A` depends on `B`, and `B` depends on `C` etc.) and handle parameters of primitive types with default values (like `$param = 'default'`). Of course, you are not allowed to have *cyclic dependencies*.
+
### Before/After Annotations
You can control execution flow with `@before` and `@after` annotations. You may move common actions into protected (non-test) methods and invoke them before or after the test method by putting them into annotations. It is possible to invoke several methods by using more than one `@before` or `@after` annotation. Methods are invoked in order from top to bottom.
diff --git a/src/Codeception/Command/Bootstrap.php b/src/Codeception/Command/Bootstrap.php
index 88b944f3d6..a7cff221c7 100644
--- a/src/Codeception/Command/Bootstrap.php
+++ b/src/Codeception/Command/Bootstrap.php
@@ -28,7 +28,7 @@ class Bootstrap extends Command
// defaults
protected $namespace = '';
protected $actorSuffix = 'Tester';
- protected $helperDir = 'tests/_support';
+ protected $supportDir = 'tests/_support';
protected $logDir = 'tests/_output';
protected $dataDir = 'tests/_data';
@@ -49,7 +49,9 @@ public function getDescription()
public function execute(InputInterface $input, OutputInterface $output)
{
- $this->namespace = rtrim($input->getOption('namespace'), '\\');
+ if ($input->getOption('namespace')) {
+ $this->namespace = trim($input->getOption('namespace'), '\\').'\\';
+ }
if ($input->getOption('actor')) {
$this->actorSuffix = $input->getOption('actor');
@@ -111,7 +113,7 @@ public function createGlobalConfig()
'tests' => 'tests',
'log' => $this->logDir,
'data' => $this->dataDir,
- 'helpers' => $this->helperDir
+ 'support' => $this->supportDir
),
'settings' => array(
'bootstrap' => '_bootstrap.php',
@@ -141,7 +143,7 @@ protected function createFunctionalSuite($actor = 'Functional')
{
$suiteConfig = array(
'class_name' => $actor.$this->actorSuffix,
- 'modules' => array('enabled' => array('Filesystem', $actor.'Helper')),
+ 'modules' => array('enabled' => array('Filesystem', "\\{$this->namespace}Helper\\$actor")),
);
$str = "# Codeception Test Suite Configuration\n\n";
@@ -157,7 +159,7 @@ protected function createAcceptanceSuite($actor = 'Acceptance')
$suiteConfig = array(
'class_name' => $actor.$this->actorSuffix,
'modules' => array(
- 'enabled' => array('PhpBrowser', $actor . 'Helper'),
+ 'enabled' => array('PhpBrowser', "\\{$this->namespace}Helper\\$actor"),
'config' => array(
'PhpBrowser' => array(
'url' => 'http://localhost/myapp/'
@@ -179,7 +181,7 @@ protected function createUnitSuite($actor = 'Unit')
{
$suiteConfig = array(
'class_name' => $actor.$this->actorSuffix,
- 'modules' => array('enabled' => array('Asserts', $actor . 'Helper')),
+ 'modules' => array('enabled' => array('Asserts', "\\{$this->namespace}Helper\\$actor")),
);
$str = "# Codeception Test Suite Configuration\n\n";
@@ -196,9 +198,10 @@ protected function createSuite($suite, $actor, $config)
"tests/$suite/_bootstrap.php",
"supportDir.DIRECTORY_SEPARATOR."Helper");
file_put_contents(
- $this->helperDir.DIRECTORY_SEPARATOR.$actor.'Helper.php',
- (new Helper($actor, $this->namespace))->produce()
+ $this->supportDir.DIRECTORY_SEPARATOR."Helper".DIRECTORY_SEPARATOR."$actor.php",
+ (new Helper($actor, rtrim($this->namespace, '\\')))->produce()
);
file_put_contents("tests/$suite.suite.yml", $config);
}
@@ -215,7 +218,7 @@ protected function createDirs()
@mkdir('tests');
@mkdir($this->logDir);
@mkdir($this->dataDir);
- @mkdir($this->helperDir);
+ @mkdir($this->supportDir);
file_put_contents($this->dataDir . '/dump.sql', '/* Replace this file with actual dump of your database */');
}
diff --git a/src/Codeception/Command/Build.php b/src/Codeception/Command/Build.php
index b21dd3e429..01d309ed8f 100644
--- a/src/Codeception/Command/Build.php
+++ b/src/Codeception/Command/Build.php
@@ -2,6 +2,7 @@
namespace Codeception\Command;
use Codeception\Configuration;
+use Codeception\Lib\Generator\Actions as ActionsGenerator;
use Codeception\Lib\Generator\Actor as ActorGenerator;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputInterface;
@@ -64,14 +65,23 @@ protected function buildActorsForConfig($configFile)
}
foreach ($suites as $suite) {
$settings = $this->getSuiteConfig($suite, $configFile);
- $gen = new ActorGenerator($settings);
- $this->output->writeln(''.Configuration::config()['namespace'].'\\'.$gen->getActorName() . " includes modules: ".implode(', ',$gen->getModules()));
- $contents = $gen->produce();
+ $actionsGenerator = new ActionsGenerator($settings);
+ $contents = $actionsGenerator->produce();
- @mkdir($settings['path'],0755, true);
- $file = $settings['path'].$this->getClassName($settings['class_name']).'.php';
+ $actorGenerator = new ActorGenerator($settings);
+ $file = $this->buildPath(Configuration::supportDir().'_generated', $settings['class_name']).$this->getClassName($settings['class_name']).'Actions.php';
$this->save($file, $contents, true);
- $this->output->writeln("{$settings['class_name']}.php generated successfully. ".$gen->getNumMethods()." methods added");
+
+ $this->output->writeln(''.Configuration::config()['namespace'].'\\'.$actorGenerator->getActorName() . " includes modules: ".implode(', ',$actorGenerator->getModules()));
+ $this->output->writeln(" -> {$settings['class_name']}Actions.php generated successfully. ".$actionsGenerator->getNumMethods()." methods added");
+
+ $contents = $actorGenerator->produce();
+
+ $file = $this->buildPath(Configuration::supportDir(), $settings['class_name']).$this->getClassName($settings['class_name']).'.php';
+ if ($this->save($file, $contents)) {
+ $this->output->writeln("{$settings['class_name']}.php created.");
+ }
+
}
}
diff --git a/src/Codeception/Command/GenerateGroup.php b/src/Codeception/Command/GenerateGroup.php
index 40dbe9dc13..31ce1574be 100644
--- a/src/Codeception/Command/GenerateGroup.php
+++ b/src/Codeception/Command/GenerateGroup.php
@@ -1,6 +1,7 @@
getArgument('group');
$class = ucfirst($group);
- $path = $this->buildPath($config['paths']['tests'].'/_groups/', $class);
- $filename = $this->completeSuffix($class, 'Group');
- $filename = $path.$filename;
+ $path = $this->buildPath(Configuration::supportDir().'Group'. DIRECTORY_SEPARATOR, $class);
- $this->introduceAutoloader($config['paths']['tests'].DIRECTORY_SEPARATOR.$config['settings']['bootstrap'], $config['namespace'], '_groups');
+ $filename = $path.$class.'.php';
$gen = new GroupGenerator($config, $group);
$res = $this->save($filename, $gen->produce());
diff --git a/src/Codeception/Command/GenerateHelper.php b/src/Codeception/Command/GenerateHelper.php
index c61bfd4935..eb3992836c 100644
--- a/src/Codeception/Command/GenerateHelper.php
+++ b/src/Codeception/Command/GenerateHelper.php
@@ -38,7 +38,7 @@ public function execute(InputInterface $input, OutputInterface $output)
{
$name = ucfirst($input->getArgument('name'));
$config = \Codeception\Configuration::config($input->getOption('config'));
- $file = \Codeception\Configuration::helpersDir() . "{$name}Helper.php";
+ $file = \Codeception\Configuration::supportDir() . "Helper\\{$name}.php";
$res = $this->save($file, (new Helper($name, $config['namespace']))->produce());
if ($res) {
diff --git a/src/Codeception/Command/GeneratePageObject.php b/src/Codeception/Command/GeneratePageObject.php
index 796628e22f..a2705a2899 100644
--- a/src/Codeception/Command/GeneratePageObject.php
+++ b/src/Codeception/Command/GeneratePageObject.php
@@ -51,11 +51,10 @@ public function execute(InputInterface $input, OutputInterface $output)
? $this->getSuiteConfig($suite, $input->getOption('config'))
: $this->getGlobalConfig($input->getOption('config'));
+ $class = ucfirst($suite) .'\\' . $class;
$className = $this->getClassName($class);
- $filename = $suite
- ? $this->pathToSuitePageObject($conf, $className)
- : $this->pathToGlobalPageObject($conf, $className);
+ $filename = $this->pathToPageObject($className, $suite);
$gen = new PageObjectGenerator($conf, $class);
$res = $this->save($filename, $gen->produce());
@@ -67,20 +66,13 @@ public function execute(InputInterface $input, OutputInterface $output)
$output->writeln("PageObject was created in $filename");
}
- protected function pathToGlobalPageObject($config, $class)
+ protected function pathToPageObject($class, $suite)
{
- $path = $this->buildPath(Configuration::projectDir().$config['paths']['tests'].'/_pages/', $class);
- $filename = $this->completeSuffix($class, 'Page');
- $this->introduceAutoloader(Configuration::projectDir().$config['paths']['tests'].DIRECTORY_SEPARATOR.$config['settings']['bootstrap'], $config['namespace'], '_pages');
- return $path.$filename;
- }
-
- protected function pathToSuitePageObject($config, $class)
- {
- $path = $this->buildPath($config['path'].'/_pages/', $class);
- $filename = $this->completeSuffix($class, 'Page');
- $this->introduceAutoloader($config['path'].DIRECTORY_SEPARATOR.$config['bootstrap'], $config['namespace'], '_pages');
- return $path.$filename;
+ if ($suite) {
+ $suite = DIRECTORY_SEPARATOR . ucfirst($suite);
+ }
+ $path = $this->buildPath(Configuration::supportDir().'Page'.$suite, $class);
+ return $path.$class.'.php';
}
}
diff --git a/src/Codeception/Command/GenerateStepObject.php b/src/Codeception/Command/GenerateStepObject.php
index 65f82e6c86..2e0bd747ec 100644
--- a/src/Codeception/Command/GenerateStepObject.php
+++ b/src/Codeception/Command/GenerateStepObject.php
@@ -1,12 +1,14 @@
getSuiteConfig($suite, $input->getOption('config'));
$class = $this->getClassName($step);
- $class = $this->removeSuffix($class, 'Steps');
- $path = $this->buildPath($config['path'].'/_steps/', $class);
- $filename = $this->completeSuffix($class, 'Steps');
- $filename = $path.$filename;
+ $path = $this->buildPath(Configuration::supportDir().'Step'. DIRECTORY_SEPARATOR . ucfirst($suite), $class);
+
+ $filename = $path.$class.'.php';
- $dialog = $this->getHelperSet()->get('dialog');
+ $helper = $this->getHelper('question');
+ $question = new Question("Add action to StepObject class (ENTER to exit): ");
- $gen = new StepObjectGenerator($config, $class);
+ $gen = new StepObjectGenerator($config, ucfirst($suite) .'\\' . $class);
if (!$input->getOption('silent')) {
do {
- $action = $dialog->ask($output, "Add action to StepObject class (ENTER to exit): ", null);
+ $action = $helper->ask($input, $output, $question);
if ($action) {
$gen->createAction($action);
}
@@ -62,13 +65,10 @@ public function execute(InputInterface $input, OutputInterface $output)
$res = $this->save($filename, $gen->produce());
- $this->introduceAutoloader($config['path'].'/'.$config['bootstrap'], trim($config['namespace'].'\\'.$config['class_name'], '\\'), '_steps');
-
if (!$res) {
$output->writeln("StepObject $filename already exists");
exit;
}
$output->writeln("StepObject was created in $filename");
}
-
}
diff --git a/src/Codeception/Configuration.php b/src/Codeception/Configuration.php
index 545a644829..b6c19f7012 100644
--- a/src/Codeception/Configuration.php
+++ b/src/Codeception/Configuration.php
@@ -3,6 +3,7 @@
namespace Codeception;
use Codeception\Exception\Configuration as ConfigurationException;
+use Codeception\Lib\Di;
use Codeception\Util\Autoload;
use Symfony\Component\Yaml\Yaml;
use Symfony\Component\Finder\Finder;
@@ -36,7 +37,7 @@ class Configuration
/**
* @var string Directory containing helpers. Helpers will be autoloaded if they have suffix "Helper".
*/
- protected static $helpersDir = null;
+ protected static $supportDir = null;
/**
* @var string Directory containing tests and suites of the current project.
@@ -144,12 +145,17 @@ public static function config($configFile = null)
throw new ConfigurationException('Data path is not defined Codeception config by key "paths: data"');
}
- if (!isset($config['paths']['helpers'])) {
- throw new ConfigurationException('Helpers path is not defined by key "paths: helpers"');
+ // compatibility with 1.x, 2.0
+ if (!isset($config['paths']['support']) and isset($config['paths']['helpers'])) {
+ $config['paths']['support'] = $config['paths']['helpers'];
+ }
+
+ if (!isset($config['paths']['support'])) {
+ throw new ConfigurationException('Helpers path is not defined by key "paths: support"');
}
self::$dataDir = $config['paths']['data'];
- self::$helpersDir = $config['paths']['helpers'];
+ self::$supportDir = $config['paths']['support'];
self::$testsDir = $config['paths']['tests'];
self::loadBootstrap($config['settings']['bootstrap']);
@@ -179,8 +185,10 @@ protected static function loadConfigFile($file, $parentConfig)
protected static function autoloadHelpers()
{
$namespace = self::$config['namespace'];
- Autoload::addNamespace($namespace, self::helpersDir());
- Autoload::addNamespace($namespace.'\Codeception\Module', self::helpersDir());
+ Autoload::addNamespace($namespace, self::supportDir());
+
+ // deprecated
+ Autoload::addNamespace($namespace.'\Codeception\Module', self::supportDir());
}
protected static function loadSuites()
@@ -286,7 +294,7 @@ public static function modules($settings)
foreach ($moduleNames as $moduleName) {
$moduleConfig = (isset($settings['modules']['config'][$moduleName])) ? $settings['modules']['config'][$moduleName] : array();
- $modules[$moduleName] = static::createModule($moduleName, $moduleConfig, $namespace);
+ $modules[$moduleName] = static::createModule($moduleName, [$moduleConfig], $namespace);
}
return $modules;
@@ -296,10 +304,8 @@ public static function modules($settings)
* Creates new module and configures it.
* Module class is searched and resolves according following rules:
*
- * 1. if "class" element is fully qualified class name, it will be taken to create module;
- * 2. module class will be searched under default namespace, according $namespace parameter:
- * $namespace.'\Codeception\Module\' . $class;
- * 3. module class will be searched under Codeception module namespace, that is "\Codeception\Module".
+ * 1. if "class" element is fully qualified class name (started with "\"), it will be taken to create module;
+ * 2. module class will be searched under Codeception module namespace, that is "\Codeception\Module".
*
* @param $class
* @param array $config module configuration
@@ -309,24 +315,25 @@ public static function modules($settings)
*/
public static function createModule($class, $config, $namespace = '')
{
+ $di = new Di();
$hasNamespace = (mb_strpos($class, '\\') !== false);
if ($hasNamespace) {
- return new $class($config);
+ return $di->instantiate($class, $config);
}
// try find module under users suite namespace setting
$className = $namespace.'\\Codeception\\Module\\' . $class;
- if (!@class_exists($className)) {
+ if (!class_exists($className)) {
// fallback to default namespace
$className = '\\Codeception\\Module\\' . $class;
- if (!@class_exists($className)) {
+ if (!class_exists($className)) {
throw new ConfigurationException($class.' could not be found and loaded');
}
}
- return new $className($config);
+ return $di->instantiate($className, $config);
}
public static function isExtensionEnabled($extensionName)
@@ -386,9 +393,9 @@ public static function dataDir()
*
* @return string
*/
- public static function helpersDir()
+ public static function supportDir()
{
- return self::$dir . DIRECTORY_SEPARATOR . self::$helpersDir . DIRECTORY_SEPARATOR;
+ return self::$dir . DIRECTORY_SEPARATOR . self::$supportDir . DIRECTORY_SEPARATOR;
}
/**
diff --git a/src/Codeception/Exception/InjectionException.php b/src/Codeception/Exception/InjectionException.php
new file mode 100644
index 0000000000..be3a1d773b
--- /dev/null
+++ b/src/Codeception/Exception/InjectionException.php
@@ -0,0 +1,7 @@
+container[$className])) {
+ if ($this->container[$className] instanceof $className) {
+ return $this->container[$className];
+ } else {
+ throw new InjectionException("Failed to resolve cyclic dependencies for class '$className'");
+ }
+ }
+ $this->container[$className] = false; // flag that object is being instantiated
+
+ $reflectedClass = new \ReflectionClass($className);
+ if (!$reflectedClass->isInstantiable()) {
+ return null;
+ }
+
+ $reflectedConstructor = $reflectedClass->getConstructor();
+ if (is_null($reflectedConstructor)) {
+ $object = new $className;
+ } else {
+ try {
+ if (!$constructorArgs) {
+ $constructorArgs = $this->prepareArgs($reflectedConstructor);
+ }
+ } catch (\Exception $e) {
+ throw new InjectionException("Failed to create instance of '$className'. ".$e->getMessage());
+ }
+ $object = $reflectedClass->newInstanceArgs($constructorArgs);
+ }
+
+ $this->injectDependencies($object, $injectMethodName);
+
+ $this->container[$className] = $object;
+ return $object;
+ }
+
+ /**
+ * @param $object
+ * @param string $injectMethodName Method which will be invoked with resolved dependencies as its arguments
+ * @throws InjectionException
+ */
+ public function injectDependencies($object, $injectMethodName = self::DEFAULT_INJECT_METHOD_NAME)
+ {
+ if (!is_object($object)) {
+ return;
+ }
+
+ $reflectedObject = new \ReflectionObject($object);
+ if (!$reflectedObject->hasMethod($injectMethodName)) {
+ return;
+ }
+
+ $reflectedMethod = $reflectedObject->getMethod($injectMethodName);
+ try {
+ $args = $this->prepareArgs($reflectedMethod);
+ } catch (\Exception $e) {
+ throw new InjectionException("Failed to inject dependencies in instance of '{$reflectedObject->name}'. ".$e->getMessage());
+ }
+
+ if (!$reflectedMethod->isPublic()) {
+ $reflectedMethod->setAccessible(true);
+ }
+ $reflectedMethod->invokeArgs($object, $args);
+ }
+
+ /**
+ * @param \ReflectionMethod $method
+ * @return array
+ * @throws \Exception
+ */
+ protected function prepareArgs(\ReflectionMethod $method)
+ {
+ $args = [];
+ $parameters = $method->getParameters();
+ foreach ($parameters as $parameter) {
+ $dependency = $parameter->getClass();
+ if (is_null($dependency)) {
+ if (!$parameter->isOptional()) {
+ throw new InjectionException("Parameter '$parameter->name' must have default value.");
+ }
+ $args[] = $parameter->getDefaultValue();
+ } else {
+ $arg = $this->instantiate($dependency->name);
+ if (is_null($arg)) {
+ throw new InjectionException("Failed to resolve dependency '{$dependency->name}'.");
+ }
+ $args[] = $arg;
+ }
+ }
+ return $args;
+ }
+}
diff --git a/src/Codeception/Lib/Generator/Actions.php b/src/Codeception/Lib/Generator/Actions.php
new file mode 100644
index 0000000000..34c4b06bcc
--- /dev/null
+++ b/src/Codeception/Lib/Generator/Actions.php
@@ -0,0 +1,200 @@
+scenario->runStep(new \Codeception\Step\{{step}}('{{method}}', func_get_args()));
+ }
+EOF;
+
+ protected $name;
+ protected $settings;
+ protected $modules;
+ protected $actions;
+ protected $numMethods = 0;
+
+ public function __construct($settings)
+ {
+ $this->name = $settings['class_name'];
+ $this->settings = $settings;
+ $this->modules = \Codeception\Configuration::modules($this->settings);
+ $this->actions = \Codeception\Configuration::actions($this->modules);
+ }
+
+
+ public function produce()
+ {
+ $namespace = rtrim($this->settings['namespace'], '\\');
+
+ $uses = [];
+ foreach ($this->modules as $module) {
+ $uses[] = "use " . get_class($module) . ";";
+ }
+
+ $methods = [];
+ $code = [];
+ foreach ($this->actions as $action => $moduleName) {
+ if (in_array($action, $methods)) {
+ continue;
+ }
+ $class = new \ReflectionClass($this->modules[$moduleName]);
+ $method = $class->getMethod($action);
+ $code[] = $this->addMethod($method);
+ $methods[] = $action;
+ $this->numMethods++;
+ }
+
+ return (new Template($this->template))
+ ->place('namespace', $namespace ? $namespace . '\\' : '')
+ ->place('hash', self::genHash($this->actions, $this->settings))
+ ->place('name', $this->name)
+ ->place('use', implode("\n", $uses))
+ ->place('methods', implode("\n\n ", $code))
+ ->produce();
+ }
+
+ protected function addMethod(\ReflectionMethod $refMethod)
+ {
+ $class = $refMethod->getDeclaringClass();
+ $params = $this->getParamsString($refMethod);
+ $module = $class->getName();
+
+ $body = '';
+ $doc = $this->addDoc($class, $refMethod);
+ $doc = str_replace('/**', '', $doc);
+ $doc = trim(str_replace('*/', '', $doc));
+ if (!$doc) {
+ $doc = "*";
+ }
+
+ $conditionalDoc = $doc . "\n * Conditional Assertion: Test won't be stopped on fail";
+
+ $methodTemplate = (new Template($this->methodTemplate))
+ ->place('module', $module)
+ ->place('method', $refMethod->name)
+ ->place('params', $params);
+
+ // generate conditional assertions
+ if (0 === strpos($refMethod->name, 'see')) {
+ $type = 'Assertion';
+ $body .= $methodTemplate
+ ->place('doc', $conditionalDoc)
+ ->place('action', 'can' . ucfirst($refMethod->name))
+ ->place('step', 'ConditionalAssertion')
+ ->produce();
+
+ // generate negative assertion
+ } elseif (0 === strpos($refMethod->name, 'dontSee')) {
+ $type = 'Assertion';
+ $body .= $methodTemplate
+ ->place('doc', $conditionalDoc)
+ ->place('action', str_replace('dont', 'cant', $refMethod->name))
+ ->place('step', 'ConditionalAssertion')
+ ->produce();
+
+ } elseif (0 === strpos($refMethod->name, 'am')) {
+ $type = 'Condition';
+ } else {
+ $type = 'Action';
+ }
+
+ $body .= $methodTemplate
+ ->place('doc', $doc)
+ ->place('action', $refMethod->name)
+ ->place('step', $type)
+ ->produce();
+
+ return $body;
+ }
+
+ /**
+ * @param \ReflectionMethod $refMethod
+ * @return array
+ */
+ protected function getParamsString(\ReflectionMethod $refMethod)
+ {
+ $params = array();
+ foreach ($refMethod->getParameters() as $param) {
+
+ if ($param->isOptional()) {
+ $params[] = '$' . $param->name . ' = null';
+ } else {
+ $params[] = '$' . $param->name;
+ };
+
+ }
+ return implode(', ', $params);
+ }
+
+ /**
+ * @param \ReflectionClass $class
+ * @param \ReflectionMethod $refMethod
+ * @return string
+ */
+ protected function addDoc(\ReflectionClass $class, \ReflectionMethod $refMethod)
+ {
+ $doc = $refMethod->getDocComment();
+
+ if (!$doc) {
+ $interfaces = $class->getInterfaces();
+ foreach ($interfaces as $interface) {
+ $i = new \ReflectionClass($interface->name);
+ if ($i->hasMethod($refMethod->name)) {
+ $doc = $i->getMethod($refMethod->name)->getDocComment();
+ break;
+ }
+ }
+ }
+
+ if (!$doc and $class->getParentClass()) {
+ $parent = new \ReflectionClass($class->getParentClass()->name);
+ if ($parent->hasMethod($refMethod->name)) {
+ $doc = $parent->getMethod($refMethod->name)->getDocComment();
+ return $doc;
+ }
+ return $doc;
+ }
+ return $doc;
+ }
+
+ public static function genHash($actions, $settings)
+ {
+ return md5(Codecept::VERSION.serialize($actions).serialize($settings['modules']));
+ }
+
+ public function getNumMethods()
+ {
+ return $this->numMethods;
+ }
+
+
+}
\ No newline at end of file
diff --git a/src/Codeception/Lib/Generator/Actor.php b/src/Codeception/Lib/Generator/Actor.php
index 5c16a119a9..90595069a2 100644
--- a/src/Codeception/Lib/Generator/Actor.php
+++ b/src/Codeception/Lib/Generator/Actor.php
@@ -1,20 +1,13 @@
scenario->runStep(new \Codeception\Step\{{step}}('{{method}}', func_get_args()));
- }
EOF;
protected $inheritedMethodTemplate = ' * @method void {{method}}({{params}})';
@@ -47,7 +32,6 @@ public function {{action}}({{params}}) {
protected $settings;
protected $modules;
protected $actions;
- protected $numMethods = 0;
public function __construct($settings)
{
@@ -60,91 +44,35 @@ public function produce()
{
$namespace = rtrim($this->settings['namespace'], '\\');
- $uses = [];
- foreach ($this->modules as $module) {
- $uses[] = "use " . get_class($module) . ";";
- }
-
- $methods = [];
- $code = [];
- foreach ($this->actions as $action => $moduleName) {
- if (in_array($action, $methods)) {
- continue;
- }
- $class = new \ReflectionClass($this->modules[$moduleName]);
- $method = $class->getMethod($action);
- $code[] = $this->addMethod($method);
- $methods[] = $action;
- $this->numMethods++;
- }
-
return (new Template($this->template))
- ->place('namespace', $namespace ? "namespace $namespace;" : '')
- ->place('hash', self::genHash($this->actions, $this->settings))
- ->place('use', implode("\n", $uses))
+ ->place('hasNamespace', $namespace ? "namespace $namespace;" : '')
->place('actor', $this->settings['class_name'])
- ->place('methods', implode("\n\n ", $code))
->place('inheritedMethods', $this->prependAbstractGuyDocBlocks())
->produce();
}
- public static function genHash($actions, $settings)
- {
- return md5(Codecept::VERSION.serialize($actions).serialize($settings['modules']));
- }
-
- protected function addMethod(\ReflectionMethod $refMethod)
+ protected function prependAbstractGuyDocBlocks()
{
- $class = $refMethod->getDeclaringClass();
- $params = $this->getParamsString($refMethod);
- $module = $class->getName();
-
- $body = '';
- $doc = $this->addDoc($class, $refMethod);
- $doc = str_replace('/**', '', $doc);
- $doc = trim(str_replace('*/', '', $doc));
- if (!$doc) {
- $doc = "*";
- }
-
- $conditionalDoc = $doc . "\n * Conditional Assertion: Test won't be stopped on fail";
-
- $methodTemplate = (new Template($this->methodTemplate))
- ->place('module', $module)
- ->place('method', $refMethod->name)
- ->place('params', $params);
+ $inherited = array();
- // generate conditional assertions
- if (0 === strpos($refMethod->name, 'see')) {
- $type = 'Assertion';
- $body .= $methodTemplate
- ->place('doc', $conditionalDoc)
- ->place('action', 'can' . ucfirst($refMethod->name))
- ->place('step', 'ConditionalAssertion')
- ->produce();
+ $class = new \ReflectionClass('\Codeception\\Actor');
+ $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
- // generate negative assertion
- } elseif (0 === strpos($refMethod->name, 'dontSee')) {
- $type = 'Assertion';
- $body .= $methodTemplate
- ->place('doc', $conditionalDoc)
- ->place('action', str_replace('dont', 'cant', $refMethod->name))
- ->place('step', 'ConditionalAssertion')
+ foreach ($methods as $method) {
+ if ($method->name == '__call') {
+ continue;
+ } // skipping magic
+ if ($method->name == '__construct') {
+ continue;
+ } // skipping magic
+ $params = $this->getParamsString($method);
+ $inherited[] = (new Template($this->inheritedMethodTemplate))
+ ->place('method', $method->name)
+ ->place('params', $params)
->produce();
-
- } elseif (0 === strpos($refMethod->name, 'am')) {
- $type = 'Condition';
- } else {
- $type = 'Action';
}
- $body .= $methodTemplate
- ->place('doc', $doc)
- ->place('action', $refMethod->name)
- ->place('step', $type)
- ->produce();
-
- return $body;
+ return implode("\n", $inherited);
}
/**
@@ -166,61 +94,6 @@ protected function getParamsString(\ReflectionMethod $refMethod)
return implode(', ', $params);
}
- /**
- * @param \ReflectionClass $class
- * @param \ReflectionMethod $refMethod
- * @return string
- */
- protected function addDoc(\ReflectionClass $class, \ReflectionMethod $refMethod)
- {
- $doc = $refMethod->getDocComment();
-
- if (!$doc) {
- $interfaces = $class->getInterfaces();
- foreach ($interfaces as $interface) {
- $i = new \ReflectionClass($interface->name);
- if ($i->hasMethod($refMethod->name)) {
- $doc = $i->getMethod($refMethod->name)->getDocComment();
- break;
- }
- }
- }
-
- if (!$doc and $class->getParentClass()) {
- $parent = new \ReflectionClass($class->getParentClass()->name);
- if ($parent->hasMethod($refMethod->name)) {
- $doc = $parent->getMethod($refMethod->name)->getDocComment();
- return $doc;
- }
- return $doc;
- }
- return $doc;
- }
-
- protected function prependAbstractGuyDocBlocks()
- {
- $inherited = array();
-
- $class = new \ReflectionClass('\Codeception\\Actor');
- $methods = $class->getMethods(\ReflectionMethod::IS_PUBLIC);
-
- foreach ($methods as $method) {
- if ($method->name == '__call') {
- continue;
- } // skipping magic
- if ($method->name == '__construct') {
- continue;
- } // skipping magic
- $params = $this->getParamsString($method);
- $inherited[] = (new Template($this->inheritedMethodTemplate))
- ->place('method', $method->name)
- ->place('params', $params)
- ->produce();
- }
-
- return implode("\n", $inherited);
- }
-
public function getActorName()
{
return $this->settings['class_name'];
@@ -231,10 +104,4 @@ public function getModules()
return array_keys($this->modules);
}
- public function getNumMethods()
- {
- return $this->numMethods;
- }
-
-
}
diff --git a/src/Codeception/Lib/Generator/Group.php b/src/Codeception/Lib/Generator/Group.php
index 646f7581e4..34d2f50e19 100644
--- a/src/Codeception/Lib/Generator/Group.php
+++ b/src/Codeception/Lib/Generator/Group.php
@@ -10,7 +10,7 @@ class Group {
protected $template = <<settings = $settings;
- $this->name = $this->removeSuffix($name, 'Group');
+ $this->name = $name;
+ $this->namespace = $this->getNamespaceString($this->settings['namespace'].'\\Group\\'.$name);
+
}
public function produce()
{
- $ns = $this->getNamespaceString($this->settings['namespace'].'\\'.$this->name);
return (new Template($this->template))
->place('class', ucfirst($this->name))
->place('name', $this->name)
- ->place('namespace', $ns)
+ ->place('namespace', $this->namespace)
+ ->place('groupName', strtolower($this->name))
->produce();
}
diff --git a/src/Codeception/Lib/Generator/Helper.php b/src/Codeception/Lib/Generator/Helper.php
index 153c60a1b7..53442dd864 100644
--- a/src/Codeception/Lib/Generator/Helper.php
+++ b/src/Codeception/Lib/Generator/Helper.php
@@ -3,16 +3,16 @@
use Codeception\Util\Template;
-class Helper {
+class Helper
+{
protected $template = <<namespace = $namespace
- ? $namespace . '\\'
- : '';
+ $this->namespace = $namespace ? "$namespace\\" : $namespace;
$this->name = $name;
}
diff --git a/src/Codeception/Lib/Generator/PageObject.php b/src/Codeception/Lib/Generator/PageObject.php
index 904ae643ce..60235cc874 100644
--- a/src/Codeception/Lib/Generator/PageObject.php
+++ b/src/Codeception/Lib/Generator/PageObject.php
@@ -10,8 +10,9 @@ class PageObject
protected $template = <<settings = $settings;
- $this->name = $this->getShortClassName($this->removeSuffix($name, 'Page'));
+ $this->name = $this->getShortClassName($name);
+ $this->namespace = $this->getNamespaceString($this->settings['namespace'].'\\'.$name);
}
public function produce()
{
- $ns = $this->getNamespaceString($this->settings['namespace'].'\\'.$this->name);
-
return (new Template($this->template))
- ->place('namespace', $ns)
+ ->place('namespace', $this->namespace)
->place('actions', $this->produceActions())
->place('class', $this->name)
->produce();
diff --git a/src/Codeception/Lib/Generator/Shared/Namespaces.php b/src/Codeception/Lib/Generator/Shared/Namespaces.php
index e7e98d45ac..4f2941725c 100644
--- a/src/Codeception/Lib/Generator/Shared/Namespaces.php
+++ b/src/Codeception/Lib/Generator/Shared/Namespaces.php
@@ -13,15 +13,14 @@ protected function getShortClassName($class)
protected function getNamespaceString($class)
{
$namespaces = $this->getNamespaces($class);
- return $namespaces
- ? 'namespace ' . implode('\\', $namespaces) . ";\n"
- : '';
+ return implode('\\', $namespaces);
}
protected function getNamespaces($class)
{
$namespaces = $this->breakParts($class);
array_pop($namespaces);
+ $namespaces = array_filter($namespaces, 'strlen');
return $namespaces;
}
diff --git a/src/Codeception/Lib/Generator/StepObject.php b/src/Codeception/Lib/Generator/StepObject.php
index 4fcd481a67..6ccf1891c3 100644
--- a/src/Codeception/Lib/Generator/StepObject.php
+++ b/src/Codeception/Lib/Generator/StepObject.php
@@ -3,24 +3,28 @@
use Codeception\Util\Template;
-class StepObject {
+class StepObject
+{
use Shared\Namespaces;
use Shared\Classname;
protected $template = <<settings = $settings;
- $this->name = $this->removeSuffix($name, 'Steps');
+ $this->name = $this->getShortClassName($name);
+ $this->namespace = $this->getNamespaceString($this->settings['namespace'].'\\Step\\'.$name);
}
public function produce()
{
$actor = $this->settings['class_name'];
- $ns = $this->getNamespaceString($this->settings['namespace'].'\\'.$actor.'\\'.$this->name);
- $ns = ltrim($ns, '\\');
-
$extended = '\\'.ltrim('\\'.$this->settings['namespace'].'\\'.$actor, '\\');
return (new Template($this->template))
- ->place('namespace', $ns)
+ ->place('namespace', $this->namespace)
->place('name', $this->name)
->place('actorClass', $extended)
->place('actions', $this->actions)
diff --git a/src/Codeception/Subscriber/AutoRebuild.php b/src/Codeception/Subscriber/AutoRebuild.php
index 83ae956d77..d030ad1ae8 100644
--- a/src/Codeception/Subscriber/AutoRebuild.php
+++ b/src/Codeception/Subscriber/AutoRebuild.php
@@ -3,7 +3,7 @@
use Codeception\Events;
use Codeception\Event\SuiteEvent;
-use Codeception\Lib\Generator\Actor;
+use Codeception\Lib\Generator\Actions;
use Codeception\SuiteManager;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
@@ -12,29 +12,29 @@ class AutoRebuild implements EventSubscriberInterface
use Shared\StaticEvents;
static $events = [
- Events::SUITE_INIT => 'updateGuy'
+ Events::SUITE_INIT => 'updateActor'
];
- public function updateGuy(SuiteEvent $e)
+ public function updateActor(SuiteEvent $e)
{
$settings = $e->getSettings();
- $guyFile = $settings['path'] . $settings['class_name'] . '.php';
+ $actorFile = $settings['path'] . DIRECTORY_SEPARATOR . '_generated' . DIRECTORY_SEPARATOR . $settings['class_name'] . 'Actions.php';
- // load guy class to see hash
- $handle = fopen($guyFile, "r");
+ // load actor class to see hash
+ $handle = @fopen($actorFile, "r");
if ($handle) {
- $line = fgets($handle);
+ $line = @fgets($handle);
if (preg_match('~\[STAMP\] ([a-f0-9]*)~', $line, $matches)) {
$hash = $matches[1];
- $currentHash = Actor::genHash(SuiteManager::$actions, $settings);
+ $currentHash = Actions::genHash(SuiteManager::$actions, $settings);
// regenerate guy class when hashes do not match
if ($hash != $currentHash) {
codecept_debug("Rebuilding {$settings['class_name']}...");
- $guyGenerator = new Actor($settings);
- fclose($handle);
- $generated = $guyGenerator->produce();
- file_put_contents($guyFile, $generated);
+ $actionsGenerator = new Actions($settings);
+ @fclose($handle);
+ $generated = $actionsGenerator->produce();
+ @file_put_contents($actorFile, $generated);
return;
}
}
diff --git a/src/Codeception/SuiteManager.php b/src/Codeception/SuiteManager.php
index aa09a0af98..e24d54c054 100644
--- a/src/Codeception/SuiteManager.php
+++ b/src/Codeception/SuiteManager.php
@@ -75,10 +75,9 @@ protected function initializeModules()
protected function initializeActors()
{
- if (!file_exists($this->path . $this->settings['class_name'] . '.php')) {
+ if (!file_exists(Configuration::supportDir() . $this->settings['class_name'] . '.php')) {
throw new Exception\Configuration($this->settings['class_name'] . " class doesn't exists in suite folder.\nRun the 'build' command to generate it");
}
- require_once $this->settings['path'] . DIRECTORY_SEPARATOR . $this->settings['class_name'] . '.php';
}
public static function hasModule($moduleName)
diff --git a/src/Codeception/TestLoader.php b/src/Codeception/TestLoader.php
index 39625c0bcd..9998d44a7a 100644
--- a/src/Codeception/TestLoader.php
+++ b/src/Codeception/TestLoader.php
@@ -2,6 +2,7 @@
namespace Codeception;
use Codeception\Lib\Parser;
+use Codeception\Lib\Di;
use Codeception\TestCase\Cept;
use Codeception\TestCase\Cest;
use Codeception\Util\Annotation;
@@ -47,6 +48,7 @@ class TestLoader {
public function __construct($path)
{
$this->path = $path;
+ $this->di = new Di;
}
public function getTests()
@@ -54,6 +56,7 @@ public function getTests()
return $this->tests;
}
+
protected function relativeName($file)
{
return $name = str_replace([$this->path, '\\'], ['', '/'], $file);
@@ -157,12 +160,11 @@ public function addCest($file)
$testClasses = Parser::getClassesFromFile($file);
foreach ($testClasses as $testClass) {
- $reflected = new \ReflectionClass($testClass);
- if ($reflected->isAbstract()) {
+ $unit = $this->di->instantiate($testClass);
+ if (!$unit) {
continue;
}
- $unit = new $testClass;
$methods = get_class_methods($testClass);
foreach ($methods as $method) {
$test = $this->createTestFromCestMethod($unit, $method, $file);
@@ -204,6 +206,7 @@ protected function enhancePhpunitTest(\PHPUnit_Framework_TestCase $test)
}
$test->initConfig();
$test->getScenario()->env(Annotation::forMethod($className, $methodName)->fetchAll('env'));
+ $this->di->injectDependencies($test);
}
protected function createTestFromCestMethod($cestInstance, $methodName, $file)
@@ -225,5 +228,4 @@ protected function createTestFromCestMethod($cestInstance, $methodName, $file)
return $cest;
}
-
}
diff --git a/tests/cli/RunCest.php b/tests/cli/RunCest.php
index f92f34d413..ce0d0d1d50 100644
--- a/tests/cli/RunCest.php
+++ b/tests/cli/RunCest.php
@@ -131,7 +131,7 @@ public function runTwoSuites(\CliGuy $I)
public function skipSuites(\CliGuy $I)
{
$I->executeCommand(
- 'run --skip skipped --skip remote --skip remote_server --skip order --skip unit --skip powers'
+ 'run --skip skipped --skip remote --skip remote_server --skip order --skip unit --skip powers --skip math'
);
$I->seeInShellOutput("Dummy Tests");
$I->dontSeeInShellOutput("Remote Tests");
@@ -184,7 +184,18 @@ public function runWithCustomOuptutPath(\CliGuy $I)
$I->seeFileFound('myownhtmlreport.html', 'tests/_log');
$I->dontSeeFileFound('report.xml','tests/_log');
$I->dontSeeFileFound('report.html','tests/_log');
+ }
+ public function runTestsWithDependencyInjections(\CliGuy $I)
+ {
+ $I->executeCommand('run math');
+ $I->seeInShellOutput('Trying to test addition (MathCest::testAddition)');
+ $I->seeInShellOutput('Trying to test subtraction (MathCest::testSubtraction)');
+ $I->seeInShellOutput('Trying to test square (MathCest::testSquare)');
+ $I->seeInShellOutput('Trying to test all (MathTest::testAll)');
+ $I->seeInShellOutput('OK (');
+ $I->dontSeeInShellOutput('fail');
+ $I->dontSeeInShellOutput('error');
}
public function runErrorTest(\CliGuy $I)
diff --git a/tests/data/FailDependenciesCyclicCest.php b/tests/data/FailDependenciesCyclicCest.php
new file mode 100644
index 0000000000..f416b73ace
--- /dev/null
+++ b/tests/data/FailDependenciesCyclicCest.php
@@ -0,0 +1,12 @@
+pi = $pi;
+ }
+
+ protected function _inject(Adder $adder, Subtractor $subtractor)
+ {
+ $this->adder = $adder;
+ $this->subtractor = $subtractor;
+ }
+
+ public function add($a, $b)
+ {
+ return $this->adder->perform($a, $b);
+ }
+
+ public function subtract($a, $b)
+ {
+ return $this->subtractor->perform($a, $b);
+ }
+
+ public function squareOfCircle($radius)
+ {
+ return $this->pi * pow($radius, 2);
+ }
+}
diff --git a/tests/data/claypit/tests/_helpers/SubtractorHelper.php b/tests/data/claypit/tests/_helpers/SubtractorHelper.php
new file mode 100644
index 0000000000..476fbb49c4
--- /dev/null
+++ b/tests/data/claypit/tests/_helpers/SubtractorHelper.php
@@ -0,0 +1,11 @@
+calc = $calc;
+ }
+
+ public function testAddition(MathTester $I)
+ {
+ $I->assertEquals(3, $this->calc->add(1, 2));
+ $I->assertEquals(0, $this->calc->add(10, -10));
+ }
+
+ public function testSubtraction(MathTester $I)
+ {
+ $I->assertEquals(1, $this->calc->subtract(3, 2));
+ $I->assertEquals(0, $this->calc->subtract(5, 5));
+ }
+
+ public function testSquare(MathTester $I)
+ {
+ $I->assertEquals(3, $this->calc->squareOfCircle(1));
+ $I->assertEquals(12, $this->calc->squareOfCircle(2));
+ }
+}
diff --git a/tests/data/claypit/tests/math/MathTest.php b/tests/data/claypit/tests/math/MathTest.php
new file mode 100644
index 0000000000..a3465d9340
--- /dev/null
+++ b/tests/data/claypit/tests/math/MathTest.php
@@ -0,0 +1,28 @@
+calc = $calc;
+ }
+
+ public function testAll()
+ {
+ $this->assertEquals(3, $this->calc->add(1, 2));
+ $this->assertEquals(1, $this->calc->subtract(3, 2));
+ $this->assertEquals(75, $this->calc->squareOfCircle(5));
+ }
+}
diff --git a/tests/data/claypit/tests/math/MathTester.php b/tests/data/claypit/tests/math/MathTester.php
new file mode 100644
index 0000000000..baebf2e447
--- /dev/null
+++ b/tests/data/claypit/tests/math/MathTester.php
@@ -0,0 +1,213 @@
+scenario->runStep(new \Codeception\Step\Action('assertEquals', func_get_args()));
+ }
+
+
+ /**
+ * [!] Method is generated. Documentation taken from corresponding module.
+ *
+ * Checks that two variables are not equal
+ *
+ * @param $expected
+ * @param $actual
+ * @param string $message
+ * @see \Codeception\Module\Asserts::assertNotEquals()
+ */
+ public function assertNotEquals($expected, $actual, $message = null) {
+ return $this->scenario->runStep(new \Codeception\Step\Action('assertNotEquals', func_get_args()));
+ }
+
+
+ /**
+ * [!] Method is generated. Documentation taken from corresponding module.
+ *
+ * Checks that expected is greater then actual
+ *
+ * @param $expected
+ * @param $actual
+ * @param string $message
+ * @see \Codeception\Module\Asserts::assertGreaterThen()
+ */
+ public function assertGreaterThen($expected, $actual, $message = null) {
+ return $this->scenario->runStep(new \Codeception\Step\Action('assertGreaterThen', func_get_args()));
+ }
+
+
+ /**
+ * [!] Method is generated. Documentation taken from corresponding module.
+ *
+ * Checks that expected is greater or equal then actual
+ *
+ * @param $expected
+ * @param $actual
+ * @param string $message
+ * @see \Codeception\Module\Asserts::assertGreaterThenOrEqual()
+ */
+ public function assertGreaterThenOrEqual($expected, $actual, $message = null) {
+ return $this->scenario->runStep(new \Codeception\Step\Action('assertGreaterThenOrEqual', func_get_args()));
+ }
+
+
+ /**
+ * [!] Method is generated. Documentation taken from corresponding module.
+ *
+ * Checks that haystack contains needle
+ *
+ * @param $needle
+ * @param $haystack
+ * @param string $message
+ * @see \Codeception\Module\Asserts::assertContains()
+ */
+ public function assertContains($needle, $haystack, $message = null) {
+ return $this->scenario->runStep(new \Codeception\Step\Action('assertContains', func_get_args()));
+ }
+
+
+ /**
+ * [!] Method is generated. Documentation taken from corresponding module.
+ *
+ * Checks that haystack doesn't contain needle.
+ *
+ * @param $needle
+ * @param $haystack
+ * @param string $message
+ * @see \Codeception\Module\Asserts::assertNotContains()
+ */
+ public function assertNotContains($needle, $haystack, $message = null) {
+ return $this->scenario->runStep(new \Codeception\Step\Action('assertNotContains', func_get_args()));
+ }
+
+
+ /**
+ * [!] Method is generated. Documentation taken from corresponding module.
+ *
+ * Checks that variable is empty.
+ *
+ * @param $actual
+ * @param string $message
+ * @see \Codeception\Module\Asserts::assertEmpty()
+ */
+ public function assertEmpty($actual, $message = null) {
+ return $this->scenario->runStep(new \Codeception\Step\Action('assertEmpty', func_get_args()));
+ }
+
+
+ /**
+ * [!] Method is generated. Documentation taken from corresponding module.
+ *
+ * Checks that variable is not empty.
+ *
+ * @param $actual
+ * @param string $message
+ * @see \Codeception\Module\Asserts::assertNotEmpty()
+ */
+ public function assertNotEmpty($actual, $message = null) {
+ return $this->scenario->runStep(new \Codeception\Step\Action('assertNotEmpty', func_get_args()));
+ }
+
+
+ /**
+ * [!] Method is generated. Documentation taken from corresponding module.
+ *
+ * Checks that variable is NULL
+ *
+ * @param $actual
+ * @param string $message
+ * @see \Codeception\Module\Asserts::assertNull()
+ */
+ public function assertNull($actual, $message = null) {
+ return $this->scenario->runStep(new \Codeception\Step\Action('assertNull', func_get_args()));
+ }
+
+
+ /**
+ * [!] Method is generated. Documentation taken from corresponding module.
+ *
+ * Checks that variable is not NULL
+ *
+ * @param $actual
+ * @param string $message
+ * @see \Codeception\Module\Asserts::assertNotNull()
+ */
+ public function assertNotNull($actual, $message = null) {
+ return $this->scenario->runStep(new \Codeception\Step\Action('assertNotNull', func_get_args()));
+ }
+
+
+ /**
+ * [!] Method is generated. Documentation taken from corresponding module.
+ *
+ * Checks that condition is positive.
+ *
+ * @param $condition
+ * @param string $message
+ * @see \Codeception\Module\Asserts::assertTrue()
+ */
+ public function assertTrue($condition, $message = null) {
+ return $this->scenario->runStep(new \Codeception\Step\Action('assertTrue', func_get_args()));
+ }
+
+
+ /**
+ * [!] Method is generated. Documentation taken from corresponding module.
+ *
+ * Checks that condition is negative.
+ *
+ * @param $condition
+ * @param string $message
+ * @see \Codeception\Module\Asserts::assertFalse()
+ */
+ public function assertFalse($condition, $message = null) {
+ return $this->scenario->runStep(new \Codeception\Step\Action('assertFalse', func_get_args()));
+ }
+
+
+ /**
+ * [!] Method is generated. Documentation taken from corresponding module.
+ *
+ * Fails the test with message.
+ *
+ * @param $message
+ * @see \Codeception\Module\Asserts::fail()
+ */
+ public function fail($message) {
+ return $this->scenario->runStep(new \Codeception\Step\Action('fail', func_get_args()));
+ }
+}
diff --git a/tests/data/claypit/tests/math/_bootstrap.php b/tests/data/claypit/tests/math/_bootstrap.php
new file mode 100644
index 0000000000..7dfa7c30f2
--- /dev/null
+++ b/tests/data/claypit/tests/math/_bootstrap.php
@@ -0,0 +1,2 @@
+assertContains('Suite shire generated', $this->output);
$helper = $this->log[1];
- $this->assertEquals(\Codeception\Configuration::helpersDir().'HobbitHelper.php',$helper['filename']);
+ $this->assertEquals(\Codeception\Configuration::supportDir().'HobbitHelper.php',$helper['filename']);
$this->assertContains('class HobbitHelper extends \Codeception\Module', $helper['content']);
$bootstrap = $this->log[0];
@@ -33,7 +33,7 @@ public function testGuyWithSuffix()
$this->assertContains('HobbitHelper',$conf['modules']['enabled']);
$helper = $this->log[1];
- $this->assertEquals(\Codeception\Configuration::helpersDir().'HobbitHelper.php',$helper['filename']);
+ $this->assertEquals(\Codeception\Configuration::supportDir().'HobbitHelper.php',$helper['filename']);
$this->assertContains('class HobbitHelper extends \Codeception\Module', $helper['content']);
}
diff --git a/tests/unit/Codeception/TestLoaderTest.php b/tests/unit/Codeception/TestLoaderTest.php
index 3fa8a12d02..c45e292972 100644
--- a/tests/unit/Codeception/TestLoaderTest.php
+++ b/tests/unit/Codeception/TestLoaderTest.php
@@ -52,24 +52,69 @@ public function testLoadFileWithFewCases()
*/
public function testLoadAllTests()
{
+ Codeception\Util\Autoload::register('Math', 'Helper', codecept_data_dir().'claypit/tests/_helpers'); // to autoload dependencies
+
$this->testLoader = new \Codeception\TestLoader(codecept_data_dir().'claypit/tests');
$this->testLoader->loadTests();
- $this->assertContainsTestName('order/AnotherCept', $this->testLoader->getTests());
- $this->assertContainsTestName('MageGuildCest::darkPower', $this->testLoader->getTests());
- $this->assertContainsTestName('FailingTest::testMe', $this->testLoader->getTests());
+
+ $testNames = $this->getTestNames($this->testLoader->getTests());
+
+ $this->assertContainsTestName('order/AnotherCept', $testNames);
+ $this->assertContainsTestName('MageGuildCest::darkPower', $testNames);
+ $this->assertContainsTestName('FailingTest::testMe', $testNames);
+ $this->assertContainsTestName('MathCest::testAddition', $testNames);
+ $this->assertContainsTestName('MathTest::testAll', $testNames);
}
- protected function assertContainsTestName($name, $tests)
+ protected function getTestNames($tests)
{
+ $testNames = [];
foreach ($tests as $test) {
if ($test instanceof \PHPUnit_Framework_TestCase) {
- $testName = \Codeception\TestCase::getTestSignature($test);
- if ($testName == $name) return;
- codecept_debug($testName);
+ $testNames[] = \Codeception\TestCase::getTestSignature($test);
}
}
- $this->fail("$name not found in tests");
+ return $testNames;
+ }
+
+ protected function assertContainsTestName($name, $testNames)
+ {
+ $this->assertNotSame(false, array_search($name, $testNames), "$name not found in tests");
+ }
+
+ public function testDependencyResolution()
+ {
+ $this->testLoader->loadTest('SimpleWithDependencyInjectionCest.php');
+ $this->assertEquals(3, count($this->testLoader->getTests()));
+ }
+
+ protected function shouldFail($msg = '')
+ {
+ $this->setExpectedException('Exception', $msg);
+ }
+
+ public function testFailDependenciesCyclic()
+ {
+ $this->shouldFail('Failed to resolve cyclic dependencies for class \'FailDependenciesCyclic\IncorrectDependenciesClass\'');
+ $this->testLoader->loadTest('FailDependenciesCyclicCest.php');
+ }
+
+ public function testFailDependenciesInChain()
+ {
+ $this->shouldFail('Failed to resolve dependency \'FailDependenciesInChain\AnotherClass\'');
+ $this->testLoader->loadTest('FailDependenciesInChainCest.php');
+ }
+
+ public function testFailDependenciesNonExistent()
+ {
+ $this->shouldFail('Class FailDependenciesNonExistent\NonExistentClass does not exist');
+ $this->testLoader->loadTest('FailDependenciesNonExistentCest.php');
+ }
+ public function testFailDependenciesPrimitiveParam()
+ {
+ $this->shouldFail('Parameter \'required\' must have default value');
+ $this->testLoader->loadTest('FailDependenciesPrimitiveParamCest.php');
}
-}
\ No newline at end of file
+}