Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Code quality, fix bug, added chained methods, added new BDD methods. (#…
…51) * improved code quality, fixed incomplete bug test, added chained methods, added new BDD methods * Fix markTestIncomplete at empty 'it' or 'should' feature. * Rename SpecifyBoostrap.php to SpecifyHooks.php
- Loading branch information
Gustavo Nieves
committed
Aug 27, 2020
1 parent
fe4825c
commit 9848be0
Showing
9 changed files
with
297 additions
and
209 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
vendor | ||
.idea | ||
.phpunit.result.cache | ||
composer.phar |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,224 +1,70 @@ | ||
<?php | ||
<?php declare(strict_types=1); | ||
|
||
namespace Codeception; | ||
|
||
use Codeception\Specify\SpecifyTest; | ||
use Codeception\Specify\ObjectProperty; | ||
use PHPUnit\Framework\AssertionFailedError; | ||
use Closure; | ||
use Codeception\Specify\SpecifyHooks; | ||
use PHPUnit\Framework\TestCase; | ||
|
||
trait Specify | ||
{ | ||
private $beforeSpecify = array(); | ||
private $afterSpecify = array(); | ||
|
||
/** | ||
* @var \DeepCopy\DeepCopy() | ||
*/ | ||
private $copier; | ||
|
||
/** | ||
* @var SpecifyTest | ||
*/ | ||
private $currentSpecifyTest; | ||
|
||
private $specifyName = ''; | ||
|
||
/** | ||
* @return SpecifyTest | ||
*/ | ||
public function getCurrentSpecifyTest() | ||
{ | ||
return $this->currentSpecifyTest; | ||
} | ||
|
||
public function should($specification, \Closure $callable = null, $params = []) | ||
{ | ||
$this->specify("should " . $specification, $callable, $params); | ||
} | ||
|
||
public function it($specification, \Closure $callable = null, $params = []) | ||
{ | ||
$this->specify($specification, $callable, $params); | ||
} | ||
|
||
public function describe($specification, \Closure $callable = null) | ||
{ | ||
$this->specify($specification, $callable); | ||
} | ||
|
||
public function specify($specification, \Closure $callable = null, $params = []) | ||
{ | ||
if (!$callable) { | ||
return; | ||
} | ||
|
||
/** @var $this TestCase **/ | ||
if (!$this->copier) { | ||
$this->copier = new \DeepCopy\DeepCopy(); | ||
$this->copier->skipUncloneable(); | ||
} | ||
|
||
$properties = $this->getSpecifyObjectProperties(); | ||
|
||
// prepare for execution | ||
$examples = $this->getSpecifyExamples($params); | ||
$showExamplesIndex = $examples !== [[]]; | ||
|
||
$specifyName = $this->specifyName; | ||
$this->specifyName .= ' ' . $specification; | ||
|
||
foreach ($examples as $idx => $example) { | ||
$test = new SpecifyTest($callable->bindTo($this)); | ||
$this->currentSpecifyTest = $test; | ||
$test->setName($this->getName() . ' |' . $this->specifyName); | ||
$test->setExample($example); | ||
if ($showExamplesIndex) { | ||
$test->setName($this->getName() . ' |' . $this->specifyName . ' # example ' . $idx); | ||
} | ||
|
||
// copy current object properties | ||
$this->specifyCloneProperties($properties); | ||
|
||
if (!empty($this->beforeSpecify) && is_array($this->beforeSpecify)) { | ||
foreach ($this->beforeSpecify as $closure) { | ||
if ($closure instanceof \Closure) $closure->__invoke(); | ||
} | ||
} | ||
|
||
$test->run($this->getTestResultObject()); | ||
$this->specifyCheckMockObjects(); | ||
|
||
// restore object properties | ||
$this->specifyRestoreProperties($properties); | ||
|
||
if (!empty($this->afterSpecify) && is_array($this->afterSpecify)) { | ||
foreach ($this->afterSpecify as $closure) { | ||
if ($closure instanceof \Closure) $closure->__invoke(); | ||
} | ||
} | ||
} | ||
|
||
// revert specify name | ||
$this->specifyName = $specifyName; | ||
} | ||
|
||
/** | ||
* @param $params | ||
* @return array | ||
* @throws \RuntimeException | ||
*/ | ||
private function getSpecifyExamples($params) | ||
{ | ||
if (isset($params['examples'])) { | ||
if (!is_array($params['examples'])) throw new \RuntimeException("Examples should be an array"); | ||
return $params['examples']; | ||
} | ||
return [[]]; | ||
use SpecifyHooks { | ||
afterSpecify as public; | ||
beforeSpecify as public; | ||
cleanSpecify as public; | ||
getCurrentSpecifyTest as public; | ||
} | ||
|
||
/** | ||
* @return \ReflectionClass|null | ||
*/ | ||
private function specifyGetPhpUnitReflection() | ||
public function specify(string $thing, Closure $code = null, $examples = []): ?self | ||
{ | ||
if ($this instanceof \PHPUnit\Framework\TestCase) { | ||
return new \ReflectionClass(\PHPUnit\Framework\TestCase::class); | ||
if ($code instanceof Closure) { | ||
$this->runSpec($thing, $code, $examples); | ||
return null; | ||
} | ||
return $this; | ||
} | ||
|
||
private function specifyCheckMockObjects() | ||
public function describe(string $feature, Closure $code = null): ?self | ||
{ | ||
if (($phpUnitReflection = $this->specifyGetPhpUnitReflection()) !== null) { | ||
$verifyMockObjects = $phpUnitReflection->getMethod('verifyMockObjects'); | ||
$verifyMockObjects->setAccessible(true); | ||
$verifyMockObjects->invoke($this); | ||
if ($code instanceof Closure) { | ||
$this->runSpec($feature, $code); | ||
return null; | ||
} | ||
return $this; | ||
} | ||
|
||
function beforeSpecify(\Closure $callable = null) | ||
{ | ||
$this->beforeSpecify[] = $callable->bindTo($this); | ||
} | ||
|
||
function afterSpecify(\Closure $callable = null) | ||
{ | ||
$this->afterSpecify[] = $callable->bindTo($this); | ||
} | ||
|
||
function cleanSpecify() | ||
public function it(string $specification, Closure $code = null, $examples = []): self | ||
{ | ||
$this->beforeSpecify = $this->afterSpecify = array(); | ||
} | ||
|
||
/** | ||
* @param ObjectProperty[] $properties | ||
*/ | ||
private function specifyRestoreProperties($properties) | ||
{ | ||
foreach ($properties as $property) { | ||
$property->restoreValue(); | ||
if ($code instanceof Closure) { | ||
$this->runSpec($specification, $code, $examples); | ||
return $this; | ||
} | ||
TestCase::markTestIncomplete(); | ||
return $this; | ||
} | ||
|
||
/** | ||
* @return ObjectProperty[] | ||
*/ | ||
private function getSpecifyObjectProperties() | ||
public function its(string $specification, Closure $code = null, $examples = []): self | ||
{ | ||
$objectReflection = new \ReflectionObject($this); | ||
$properties = $objectReflection->getProperties(); | ||
|
||
if (($classProperties = $this->specifyGetClassPrivateProperties()) !== []) { | ||
$properties = array_merge($properties, $classProperties); | ||
} | ||
|
||
$clonedProperties = []; | ||
|
||
foreach ($properties as $property) { | ||
/** @var $property \ReflectionProperty **/ | ||
$docBlock = $property->getDocComment(); | ||
if (!$docBlock) { | ||
continue; | ||
} | ||
if (preg_match('~\*(\s+)?@specify\s?~', $docBlock)) { | ||
$property->setAccessible(true); | ||
$clonedProperties[] = new ObjectProperty($this, $property); | ||
} | ||
} | ||
|
||
// isolate mockObjects property from PHPUnit\Framework\TestCase | ||
if ($classReflection = $this->specifyGetPhpUnitReflection()) { | ||
$property = $classReflection->getProperty('mockObjects'); | ||
// remove all mock objects inherited from parent scope(s) | ||
$clonedProperties[] = new ObjectProperty($this, $property); | ||
$property->setValue($this, []); | ||
} | ||
|
||
return $clonedProperties; | ||
return $this->it($specification, $code, $examples); | ||
} | ||
|
||
private function specifyGetClassPrivateProperties() | ||
public function should(string $behavior, Closure $code = null, $examples = []): self | ||
{ | ||
static $properties = []; | ||
|
||
if (!isset($properties[__CLASS__])) { | ||
$reflection = new \ReflectionClass(__CLASS__); | ||
|
||
$properties[__CLASS__] = (get_class($this) !== __CLASS__) | ||
? $reflection->getProperties(\ReflectionProperty::IS_PRIVATE) : []; | ||
if ($code instanceof Closure) { | ||
$this->runSpec('should ' . $behavior, $code, $examples); | ||
return $this; | ||
} | ||
|
||
return $properties[__CLASS__]; | ||
TestCase::markTestIncomplete(); | ||
return $this; | ||
} | ||
|
||
/** | ||
* @param ObjectProperty[] $properties | ||
*/ | ||
private function specifyCloneProperties($properties) | ||
public function shouldNot(string $behavior, Closure $code = null, $examples = []): self | ||
{ | ||
foreach ($properties as $property) { | ||
$propertyValue = $property->getValue(); | ||
$property->setValue($this->copier->copy($propertyValue)); | ||
if ($code instanceof Closure) { | ||
$this->runSpec('should not ' . $behavior, $code, $examples); | ||
return $this; | ||
} | ||
TestCase::markTestIncomplete(); | ||
return $this; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.