Skip to content

Commit

Permalink
Shared Examples are now run with the example group behaving like it
Browse files Browse the repository at this point in the history
  • Loading branch information
MarcelloDuarte committed Jul 25, 2012
1 parent bf5d058 commit 8ac8438
Show file tree
Hide file tree
Showing 11 changed files with 260 additions and 45 deletions.
43 changes: 30 additions & 13 deletions features/example_groups/shared_examples.feature
Original file line number Diff line number Diff line change
@@ -1,37 +1,54 @@
Feature: Developer can reuse examples from another spec
As a developer
I can use a shared example
So that I don't need to repeat the description of behaviour that is common to
more than one type
So that I don't need to repeat descriptions common to more than one group

Scenario: shared examples group included in one file
Given a file named "ACollectionSpec.php" with:
Given a file named "MyArraySpec.php" with:
"""
<?php
class Collection {
protected $items = array();
public function add() {
$this->items = array_merge($this->items, func_get_args());
}
public function size() {
return count($this->items);
}
}
class MyArray extends Collection {}
use PHPSpec\Specification\SharedExample;
class ACollectionSharedExamples extends SharedExample
class ACollection extends SharedExample
{
protected $object;
function before()
{
$this->collection = new Collection('one', 'two', 'three');
}
$this->object = $this->spec(new Collection);
}
function itSaysItHasThreeItems()
{
$this->collection->size()->should->equal(3);
$this->object->add('one', 'two', 'three');
$this->object->size()->should->equal(3);
}
}
"""
And a file named "MyArraySpec.php" with:
"""
<?php
use PHPSpec\Context;
class DescribeMyArray extends Context
{
public $itBehavesLike = 'a collection';
public $itBehavesLike = 'ACollection';
protected $object;
function before()
{
$this->object = $this->spec(new MyArray);
}
}
"""
When I run "phpspec MyArraySpec.php -f d"
Expand Down
6 changes: 3 additions & 3 deletions spec/Specification/ExampleGroupSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,20 +15,20 @@ function before()
$this->example = $this->spec(new \DescribeSomeContext);
}

public function itDoesntBehaveLikeAnotherObjectByDefault()
function itDoesntBehaveLikeAnotherObjectByDefault()
{

$this->example->behavesLikeAnotherObject()->should->beFalse();
}

public function itCanBehaveLikeAnotherObject()
function itCanBehaveLikeAnotherObject()
{
$this->example = $this->spec(clone $this);
$this->example->itBehavesLike = '\SomeSharedExample';
$this->example->behavesLikeAnotherObject()->should->beTrue();
}

public function itCantBehaveLikeAnObjectThatIsNotASharedExample()
function itCantBehaveLikeAnObjectThatIsNotASharedExample()
{
$example = new \DescribeSomeContext;
$example->itBehavesLike = '\SomethingElse';
Expand Down
11 changes: 11 additions & 0 deletions spec/Specification/ExampleRunnerSpec.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

use \PHPSpec\Specification\ExampleRunner;

require_once __DIR__ . '/_files/BehaveLikeAnotherSpec.php';

class DescribeExampleRunner extends \PHPSpec\Context
{
const NUM_OF_METHODS_EXAMPLE_HAS = 2;
Expand All @@ -28,11 +30,20 @@ function itWillCallCreateForEachExampleOfTheGroup()
$this->exampleRunner->run(new \Fake, $this->reporter);
}

function itWillIncludeBehavioursShared()
{
$exampleGroup = new \DescribeBehaveLikeAnother;
$this->exampleFactory->shouldReceive('create')
->times(2);
$this->exampleRunner->run($exampleGroup, $this->reporter);
}

function getReporter()
{
$reporter = $this->mock('PHPSpec\Runner\Reporter');
$reporter->shouldReceive('exampleGroupStarted');
$reporter->shouldReceive('exampleGroupFinished');
$reporter->shouldIgnoreMissing();
return $reporter;
}

Expand Down
8 changes: 8 additions & 0 deletions spec/Specification/_files/BehaveLikeAnotherSpec.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

use PHPSpec\Context;

class DescribeBehaveLikeAnother extends Context
{
public $itBehavesLike = '\SomeSharedExample';
}
10 changes: 9 additions & 1 deletion spec/Specification/_files/SomeSharedExample.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,13 @@

class SomeSharedExample extends SharedExample
{
function itHasSomeExample()
{
'not empty';
}

}
function itHasAnotherExample()
{
'not empty';
}
}
77 changes: 52 additions & 25 deletions src/PHPSpec/Specification/Example.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,16 @@
*/
namespace PHPSpec\Specification;

use \PHPSpec\Runner\Reporter,
\PHPSpec\Specification\Result\Exception,
\PHPSpec\Specification\Result\Error,
\PHPSpec\Specification\Result\Pending,
\PHPSpec\Specification\Result\DeliberateFailure,
\PHPSpec\Specification\Result\Failure,
\PHPSpec\Util\Filter,
\PHPSpec\Util\ReflectionMethod;
use PHPSpec\Specification\Result\Exception;
use PHPSpec\Specification\Result\Error;
use PHPSpec\Specification\Result\Pending;
use PHPSpec\Specification\Result\DeliberateFailure;
use PHPSpec\Specification\Result\Failure;

use PHPSpec\Runner\Reporter;

use PHPSpec\Util\Filter;
use PHPSpec\Util\ReflectionMethod;

/**
* @category PHPSpec
Expand All @@ -46,63 +48,75 @@ class Example
* @var string
*/
protected $_methodName;

/**
* The example group
*
* @var PHPSpec\Specification\ExampleGroup
*/
protected $_exampleGroup;

/**
* Represents the execution time of the example
*
* @var integer
*/
protected $_executionTime;

/**
* Example keeps a reference to the example group and is created with the
* example as a reflected method
*
* @param PHPSpec\Specification\ExampleGroup $exampleGroup
* @param string $methodName
*/
public function __construct (ExampleGroup $exampleGroup, $methodName)
public function __construct(ExampleGroup $exampleGroup, $methodName)
{
$this->_methodName = $methodName;
$this->_exampleGroup = $exampleGroup;
}

/**
* Runs the example
*
* @param \PHPSpec\Runner\Reporter $reporter
* @param PHPSpec\Runner\Reporter $reporter
*/
public function run (Reporter $reporter)
public function run(Reporter $reporter)
{
try {
$startTime = microtime(true);
call_user_func(array($this->_exampleGroup, 'before'));
$this->markExampleAsPendingIfItIsEmpty();
call_user_func(array($this->_exampleGroup, $this->_methodName));
$this->closeExample($startTime);
$this->runExample($reporter);
$this->closeExample($startTime, $reporter);
} catch (Failure $failure) {
$reporter->addFailure($this, $failure);
$this->closeExample($startTime);
$this->closeExample($startTime, $reporter);
return;
} catch (Pending $pending) {
$reporter->addPending($this, $pending);
$this->closeExample($startTime);
$this->closeExample($startTime, $reporter);
return;
} catch (Error $error) {
$reporter->addError($this, $error);
$this->closeExample($startTime);
$this->closeExample($startTime, $reporter);
return;
} catch (\Exception $e) {
$reporter->addException($this, new Exception($e));
$this->closeExample($startTime);
$this->closeExample($startTime, $reporter);
return;
}
$reporter->addPass($this);
}

/**
* Runs the example
*/
protected function runExample($reporter)
{
call_user_func(array($this->_exampleGroup, $this->_methodName));
}

/**
* Gets the description in the following format:
*
Expand All @@ -112,11 +126,12 @@ public function run (Reporter $reporter)
*
* @return string
*/
public function getDescription ()
public function getDescription()
{
$class = str_replace('Describe', '', get_class($this->_exampleGroup));
return "$class " . $this->getSpecificationText();
}

/**
* Return the specification text taken from method name
*
Expand All @@ -127,36 +142,39 @@ public function getDescription ()
* @param string $methodName
* @return string
*/
public function getSpecificationText ()
public function getSpecificationText()
{
$methodName = substr($this->_methodName, 2);
return Filter::camelCaseToSpace($methodName);
}

/**
* Returns the method name of the testcase. This method is used in the
* junit formatter.
*
* @return string
*/
public function getMethodName ()
public function getMethodName()
{
return $this->_methodName;
}

/**
* Returns the example group. This method is used in the junit formatter.
*
* @return ExampleGroup|PHPSpec\Specification\ExampleGroup
*/
public function getExampleGroup ()
public function getExampleGroup()
{
return $this->_exampleGroup;
}

/**
* Returns the execution time for this example.
*
* @return float
*/
public function getExecutionTime ()
public function getExecutionTime()
{
return $this->_executionTime;
}
Expand All @@ -165,20 +183,29 @@ public function getExecutionTime ()
* Closes example
*
*/
private function closeExample($startTime)
private function closeExample($startTime, $reporter = null)
{
call_user_func(array($this->_exampleGroup, 'after'));
$endTime = microtime(true);
$this->_executionTime = $endTime - $startTime;
if (class_exists('Mockery')) {
\Mockery::close();
try {
\Mockery::close();
} catch (\Mockery\CountValidator\Exception $e) {
$failure = new Failure($e->getMessage());
if ($reporter->getFailures() === null) {
$reporter->setFailures(new \SplObjectStorage);
}
$reporter->addFailure($this, $failure);
}

}
}

/**
* Marks example as pending if it is empty
*/
private function markExampleAsPendingIfItIsEmpty()
protected function markExampleAsPendingIfItIsEmpty()
{
$method = new ReflectionMethod(
$this->_exampleGroup, $this->_methodName
Expand Down
11 changes: 10 additions & 1 deletion src/PHPSpec/Specification/ExampleFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
*/
namespace PHPSpec\Specification;

use PHPSpec\Specification\SharedExample\Example as Shared;

/**
* @category PHPSpec
* @package PHPSpec
Expand All @@ -41,6 +43,13 @@ class ExampleFactory
*/
public function create(ExampleGroup $exampleGroup, $example)
{
return new Example($exampleGroup, $example);
if (method_exists($exampleGroup, $example)) {
return new Example($exampleGroup, $example);
} elseif ($exampleGroup->hasSharedExample($example)) {
return new Shared(
$exampleGroup, $exampleGroup->getSharedExample($example), $example
);
}
die('hard');
}
}
Loading

0 comments on commit 8ac8438

Please sign in to comment.