Skip to content

Commit

Permalink
moved all support classes to _support dir
Browse files Browse the repository at this point in the history
  • Loading branch information
DavertMik committed Feb 19, 2015
1 parent 376319f commit 1582242
Show file tree
Hide file tree
Showing 37 changed files with 1,053 additions and 284 deletions.
80 changes: 80 additions & 0 deletions docs/07-AdvancedUsage.md
Expand Up @@ -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
<?php
use \AcceptanceTester;

class SignUpCest
{
/**
* @var SignUpHelper
*/
protected $signUp;

/**
* @var NavBarHelper
*/
protected $navBar;

protected function _inject(SignUpHelper $signUp, NavBarHelper $navBar)
{
$this->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:

```
<?php
class MathTest extends \Codeception\TestCase\Test
{
/**
* @var \UnitTester
*/
protected $tester;
/**
* @var \MathHelper
*/
protected $math;
protected function _inject(\MathHelper $math)
{
$this->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.
Expand Down
21 changes: 12 additions & 9 deletions src/Codeception/Command/Bootstrap.php
Expand Up @@ -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';

Expand All @@ -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');
Expand Down Expand Up @@ -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',
Expand Down Expand Up @@ -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";
Expand All @@ -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/'
Expand All @@ -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";
Expand All @@ -196,9 +198,10 @@ protected function createSuite($suite, $actor, $config)
"tests/$suite/_bootstrap.php",
"<?php\n// Here you can initialize variables that will be available to your tests\n"
);
@mkdir($this->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);
}
Expand All @@ -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 */');
}

Expand Down
22 changes: 16 additions & 6 deletions src/Codeception/Command/Build.php
Expand Up @@ -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;
Expand Down Expand Up @@ -64,14 +65,23 @@ protected function buildActorsForConfig($configFile)
}
foreach ($suites as $suite) {
$settings = $this->getSuiteConfig($suite, $configFile);
$gen = new ActorGenerator($settings);
$this->output->writeln('<info>'.Configuration::config()['namespace'].'\\'.$gen->getActorName() . "</info> 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('<info>'.Configuration::config()['namespace'].'\\'.$actorGenerator->getActorName() . "</info> 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.");
}

}
}

Expand Down
7 changes: 3 additions & 4 deletions src/Codeception/Command/GenerateGroup.php
@@ -1,6 +1,7 @@
<?php
namespace Codeception\Command;

use Codeception\Configuration;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
Expand Down Expand Up @@ -36,11 +37,9 @@ public function execute(InputInterface $input, OutputInterface $output)
$group = $input->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());
Expand Down
2 changes: 1 addition & 1 deletion src/Codeception/Command/GenerateHelper.php
Expand Up @@ -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) {
Expand Down
24 changes: 8 additions & 16 deletions src/Codeception/Command/GeneratePageObject.php
Expand Up @@ -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());
Expand All @@ -67,20 +66,13 @@ public function execute(InputInterface $input, OutputInterface $output)
$output->writeln("<info>PageObject was created in $filename</info>");
}

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';
}

}
22 changes: 11 additions & 11 deletions src/Codeception/Command/GenerateStepObject.php
@@ -1,12 +1,14 @@
<?php
namespace Codeception\Command;

use Codeception\Configuration;
use Codeception\Lib\Generator\StepObject as StepObjectGenerator;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\Console\Question\Question;

/**
* Generates StepObject class. You will be asked for steps you want to implement.
Expand All @@ -30,7 +32,8 @@ protected function configure()
));
}

public function getDescription() {
public function getDescription()
{
return 'Generates empty StepObject class';
}

Expand All @@ -41,19 +44,19 @@ public function execute(InputInterface $input, OutputInterface $output)
$config = $this->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);
}
Expand All @@ -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("<error>StepObject $filename already exists</error>");
exit;
}
$output->writeln("<info>StepObject was created in $filename</info>");
}

}

0 comments on commit 1582242

Please sign in to comment.