Skip to content

Commit

Permalink
added dry-run command
Browse files Browse the repository at this point in the history
  • Loading branch information
DavertMik committed Apr 25, 2016
1 parent 061cd3b commit 96138c3
Show file tree
Hide file tree
Showing 23 changed files with 229 additions and 33 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
* **Params** loading from yml, env files or environment. [Announcement](http://codeception.com/03-05-2016/codeception-2.2.-upcoming-features.html#params)
* **Test dependencies** with `@depends` annotation. [Announcement](http://codeception.com/03-05-2016/codeception-2.2.-upcoming-features.html#test-dependencies)
* **Custom Commands** inject your own commands as as simple as extension. [Announcement](http://codeception.com/03-10-2016/even-more-features-of-codeception.html#custom-commands)
* `codecept dry-run` command added to show scenario steps without executing them.
* [WebDriver] allows getting current browser and capabilities in test. [Announcement](http://codeception.com/03-10-2016/even-more-features-of-codeception.html#Getting-current-browser-and-capabilities-in-tests)
* [AngularJS] module added. Extends WebDriver module for AngularJS testing. [Announcement](http://codeception.com/03-10-2016/even-more-features-of-codeception.html#angularjs)
* [DataFactory] module added. Performs data generation using FactoryMuffin library [Announcement](http://codeception.com/03-10-2016/even-more-features-of-codeception.html#datafactory)
Expand Down
1 change: 1 addition & 0 deletions codecept
Original file line number Diff line number Diff line change
Expand Up @@ -28,5 +28,6 @@ $app->add(new Codeception\Command\GenerateEnvironment('generate:environment'));
$app->add(new Codeception\Command\GenerateFeature('generate:feature'));
$app->add(new Codeception\Command\GherkinSnippets('gherkin:snippets'));
$app->add(new Codeception\Command\GherkinSteps('gherkin:steps'));
$app->add(new Codeception\Command\DryRun('dry-run'));
$app->registerCustomCommands();
$app->run();
136 changes: 136 additions & 0 deletions src/Codeception/Command/DryRun.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
<?php
namespace Codeception\Command;

use Codeception\Configuration;
use Codeception\Event\SuiteEvent;
use Codeception\Event\TestEvent;
use Codeception\Events;
use Codeception\Subscriber\Bootstrap as BootstrapLoader;
use Codeception\Subscriber\Console as ConsolePrinter;
use Codeception\SuiteManager;
use Codeception\Test\Interfaces\ScenarioDriven;
use Codeception\Test\Loader;
use Codeception\Test\Test;
use Codeception\Util\Maybe;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Output\OutputInterface;
use Symfony\Component\EventDispatcher\EventDispatcher;

/**
* Shows step by step execution process for scenario driven tests without actually running them.
*
* * `codecept dry-run acceptance`
* * `codecept dry-run acceptance MyCest`
* * `codecept dry-run acceptance checkout.feature`
* * `codecept dry-run tests/acceptance/MyCest.php`
*
*/
class DryRun extends Command
{
use Shared\Config;
use Shared\Style;

protected function configure()
{
$this->setDefinition(
[
new InputArgument('suite', InputArgument::REQUIRED, 'suite to scan for feature files'),
new InputArgument('test', InputArgument::OPTIONAL, 'tests to be loaded'),
new InputOption('config', 'c', InputOption::VALUE_OPTIONAL, 'Use custom path for config'),
]
);
parent::configure();
}

public function getDescription()
{
return 'Prints step-by-step scenario-driven test or a feature';
}

public function execute(InputInterface $input, OutputInterface $output)
{
$this->addStyles($output);
$suite = $input->getArgument('suite');
$test = $input->getArgument('test');

$config = Configuration::config( $input->getOption('config'));
if (! Configuration::isEmpty() && ! $test && strpos($suite, $config['paths']['tests']) === 0) {
list(, $suite, $test) = $this->matchTestFromFilename($suite, $config['paths']['tests']);
}
$settings = $this->getSuiteConfig($suite, $input->getOption('config'));

$dispatcher = new EventDispatcher();
$dispatcher->addSubscriber(new ConsolePrinter([
'colors' => $input->getOption('ansi'),
'steps' => true,
'verbosity' => OutputInterface::VERBOSITY_VERBOSE,
]));
$dispatcher->addSubscriber(new BootstrapLoader());

$suiteManager = new SuiteManager($dispatcher, $suite, $settings);
$moduleContainer = $suiteManager->getModuleContainer();
foreach (Configuration::modules($settings) as $module) {
$moduleContainer->mock($module, new Maybe());
}
$suiteManager->loadTests($test);
$tests = $suiteManager->getSuite()->tests();

$dispatcher->dispatch(Events::SUITE_INIT, new SuiteEvent($suiteManager->getSuite(), null, $settings));
$dispatcher->dispatch(Events::SUITE_BEFORE, new SuiteEvent($suiteManager->getSuite(), null, $settings));
foreach ($tests as $test) {
if ($test instanceof \PHPUnit_Framework_TestSuite_DataProvider) {
foreach ($test as $t) {
if ($t instanceof Test) {
$this->dryRunTest($output, $dispatcher, $t);
}
}
}
if ($test instanceof Test and $test instanceof ScenarioDriven) {
$this->dryRunTest($output, $dispatcher, $test);
}
}
$dispatcher->dispatch(Events::SUITE_AFTER, new SuiteEvent($suiteManager->getSuite()));
}


protected function matchTestFromFilename($filename, $tests_path)
{
$filename = str_replace(['//', '\/', '\\'], '/', $filename);
$res = preg_match("~^$tests_path/(.*?)/(.*)$~", $filename, $matches);
if (!$res) {
throw new \InvalidArgumentException("Test file can't be matched");
}

return $matches;
}

/**
* @param OutputInterface $output
* @param $dispatcher
* @param $test
*/
protected function dryRunTest(OutputInterface $output, EventDispatcher $dispatcher, Test $test)
{
$dispatcher->dispatch(Events::TEST_START, new TestEvent($test));
$dispatcher->dispatch(Events::TEST_BEFORE, new TestEvent($test));
try {
$test->test();
} catch (\Exception $e) {
}
$dispatcher->dispatch(Events::TEST_AFTER, new TestEvent($test));
$dispatcher->dispatch(Events::TEST_END, new TestEvent($test));
if ($test->getMetadata()->isBlocked()) {
$output->writeln('');
if ($skip = $test->getMetadata()->getSkip()) {
$output->writeln("<warning> SKIPPED </warning>" . $skip);
}
if ($incomplete = $test->getMetadata()->getIncomplete()) {
$output->writeln("<warning> INCOMPLETE </warning>" . $incomplete);
}
}
$output->writeln('');
}
}
9 changes: 9 additions & 0 deletions src/Codeception/Command/Run.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,15 @@
/**
* Executes tests.
*
* Usage:
*
* * `codecept run acceptance` - run all acceptance tests
* * `codecept run tests/acceptance/MyCept.php` - run only MyCept
* * `codecept run acceptance MyCept` - same as above
* * `codecept run acceptance MyCest:myTestInIt` - run one test from a Cest
* * `codecept run acceptance checkout.feature` - run feature-file
*
* Full reference:
* ```
* Arguments:
* suite suite to be tested
Expand Down
2 changes: 2 additions & 0 deletions src/Codeception/Command/Shared/Style.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,7 @@ public function addStyles(OutputInterface $output)
$output->getFormatter()->setStyle('notice', $style);
$style = new OutputFormatterStyle(null, null, ['bold']);
$output->getFormatter()->setStyle('bold', $style);
$style = new OutputFormatterStyle(null, 'yellow', ['bold']);
$output->getFormatter()->setStyle('warning', $style);
}
}
4 changes: 2 additions & 2 deletions src/Codeception/Lib/Console/Output.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ class Output extends ConsoleOutput

protected $isInteractive = false;

function __construct($config)
public function __construct($config)
{
$this->config = array_merge($this->config, $config);

Expand Down Expand Up @@ -72,7 +72,7 @@ public function debug($message)
$this->writeln("<debug> $message</debug>");
}

function message($message)
public function message($message)
{
$message = call_user_func_array('sprintf', func_get_args());
return new Message($message, $this);
Expand Down
5 changes: 5 additions & 0 deletions src/Codeception/Lib/ModuleContainer.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,11 @@ public function all()
return $this->modules;
}

public function mock($moduleName, $mock)
{
$this->modules[$moduleName] = $mock;
}

private function instantiate($name, $class, $config)
{
$module = $this->di->instantiate($class, [$this, $config], false);
Expand Down
19 changes: 8 additions & 11 deletions src/Codeception/PHPUnit/ResultPrinter.php
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
<?php
namespace Codeception\PHPUnit;

use Codeception\Test\Descriptor;
use Codeception\Test\Interfaces\ScenarioDriven;
use Exception;
use PHPUnit_Framework_AssertionFailedError;
use PHPUnit_Framework_Test;
use PHPUnit_Runner_BaseTestRunner;
Expand All @@ -14,10 +11,10 @@ class ResultPrinter extends \PHPUnit_Util_TestDox_ResultPrinter
* An error occurred.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param \Exception $e
* @param float $time
*/
public function addError(PHPUnit_Framework_Test $test, Exception $e, $time)
public function addError(PHPUnit_Framework_Test $test, \Exception $e, $time)
{
$this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_ERROR;
$this->failed++;
Expand All @@ -40,10 +37,10 @@ public function addFailure(PHPUnit_Framework_Test $test, PHPUnit_Framework_Asser
* Incomplete test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param \Exception $e
* @param float $time
*/
public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $time)
public function addIncompleteTest(PHPUnit_Framework_Test $test, \Exception $e, $time)
{
$this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_INCOMPLETE;
$this->incomplete++;
Expand All @@ -53,12 +50,12 @@ public function addIncompleteTest(PHPUnit_Framework_Test $test, Exception $e, $t
* Risky test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param \Exception $e
* @param float $time
*
* @since Method available since Release 4.0.0
*/
public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
public function addRiskyTest(PHPUnit_Framework_Test $test, \Exception $e, $time)
{
$this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_RISKY;
$this->risky++;
Expand All @@ -68,12 +65,12 @@ public function addRiskyTest(PHPUnit_Framework_Test $test, Exception $e, $time)
* Skipped test.
*
* @param PHPUnit_Framework_Test $test
* @param Exception $e
* @param \Exception $e
* @param float $time
*
* @since Method available since Release 3.0.0
*/
public function addSkippedTest(PHPUnit_Framework_Test $test, Exception $e, $time)
public function addSkippedTest(PHPUnit_Framework_Test $test, \Exception $e, $time)
{
$this->testStatus = PHPUnit_Runner_BaseTestRunner::STATUS_SKIPPED;
$this->skipped++;
Expand Down
25 changes: 17 additions & 8 deletions src/Codeception/Subscriber/Console.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ class Console implements EventSubscriberInterface
protected $message = null;
protected $steps = true;
protected $debug = false;
protected $color = true;
protected $ansi = true;
protected $silent = false;
protected $lastTestFailed = false;
Expand All @@ -59,25 +58,35 @@ class Console implements EventSubscriberInterface
protected $traceLength = 5;
protected $width;
protected $output;
protected $options;
protected $fails = [];
protected $reports = [];
protected $namespace = '';
protected $chars = ['success' => '+', 'fail' => 'x', 'of' => ':'];

protected $options = [
'debug' => false,
'ansi' => false,
'steps' => true,
'verbosity' => 0,
'xml' => null,
'html' => null,
'tap' => null,
'json' => null,
];

public function __construct($options)
{
$this->options = $options;
$this->debug = $options['debug'] || $options['verbosity'] >= OutputInterface::VERBOSITY_VERY_VERBOSE;
$this->steps = $this->debug || $options['steps'];
$this->rawStackTrace = ($options['verbosity'] === OutputInterface::VERBOSITY_DEBUG);
$this->options = array_merge($this->options, $options);
$this->debug = $this->options['debug'] || $this->options['verbosity'] >= OutputInterface::VERBOSITY_VERY_VERBOSE;
$this->steps = $this->debug || $this->options['steps'];
$this->rawStackTrace = ($this->options['verbosity'] === OutputInterface::VERBOSITY_DEBUG);
$this->output = new Output($options);
if ($this->debug) {
Debug::setOutput($this->output);
}
$this->detectWidth();

if ($options['ansi'] && !$this->isWin()) {
if ($this->options['ansi'] && !$this->isWin()) {
$this->chars['success'] = '';
$this->chars['fail'] = '';
}
Expand Down Expand Up @@ -246,7 +255,7 @@ public function testIncomplete(FailEvent $e)
protected function isDetailed($test)
{
if ($test instanceof ScenarioDriven && $this->steps) {
return !$test->getMetadata()->isBlocked();
return true;
}
return false;
}
Expand Down
1 change: 0 additions & 1 deletion src/Codeception/SuiteManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,6 @@ public function run(PHPUnit\Runner $runner, \PHPUnit_Framework_TestResult $resul
$this->dispatcher->dispatch(Events::SUITE_AFTER, new Event\SuiteEvent($this->suite, $result, $this->settings));
}


/**
* @return \Codeception\Suite
*/
Expand Down
1 change: 1 addition & 0 deletions src/Codeception/Test/Cest.php
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ protected function executeAfterMethods($testMethod, $I)
}
}
}

protected function executeContextMethod($context, $I)
{
if (method_exists($this->testClassInstance, $context)) {
Expand Down
3 changes: 2 additions & 1 deletion src/Codeception/Test/Gherkin.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,8 @@ protected function validateStep(StepNode $stepNode)
}
return;
}
$this->getMetadata()->setIncomplete("Step definition for `$stepText` not found in contexts");
$incomplete = $this->getMetadata()->getIncomplete();
$this->getMetadata()->setIncomplete("$incomplete\nStep definition for `$stepText` not found in contexts");
}

protected function runStep(StepNode $stepNode)
Expand Down
26 changes: 26 additions & 0 deletions tests/cli/DryRunCest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php
class DryRunCest
{
public function _before(CliGuy $I)
{
$I->amInPath('tests/data/sandbox');
}

public function runCestWithExamples(CliGuy $I)
{
$I->executeCommand('dry-run scenario ExamplesCest --no-ansi');
$I->seeInShellOutput('ExamplesCest: Files exists annotation');
}

public function runFeature(CliGuy $I)
{
$I->executeCommand('dry-run scenario File.feature --no-ansi');
$I->seeInShellOutput('Run gherkin: Check file exists');
$I->seeInShellOutput('In order to test a feature');
$I->seeInShellOutput('As a user');
$I->seeInShellOutput('Given i have terminal opened');
$I->seeInShellOutput('INCOMPLETE');
$I->seeInShellOutput('Step definition for `I have only idea of what\'s going on here` not found');

}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php //[STAMP] 38eabe0414da90f02725c1fbfcb9338e
<?php //[STAMP] fa706bf334361b0560681a26e0641fd4
namespace Jazz\Pianist\_generated;

// This class was automatically generated by build task
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?php //[STAMP] 39561558f77d4653cbbf4684d8dc6701
<?php //[STAMP] 1f196b85b923a69ed7f6e582ff6ce08c
namespace Jazz\_generated;

// This class was automatically generated by build task
Expand Down
Loading

0 comments on commit 96138c3

Please sign in to comment.