Skip to content

Commit 9848be0

Browse files
author
Gustavo Nieves
authored
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
1 parent fe4825c commit 9848be0

File tree

9 files changed

+297
-209
lines changed

9 files changed

+297
-209
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
vendor
22
.idea
3+
.phpunit.result.cache
34
composer.phar

composer.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
{
88
"name": "Michael Bodnarchuk",
99
"email": "[email protected]"
10+
},
11+
{
12+
"name": "Gustavo Nieves",
13+
"homepage": "https://medium.com/@ganieves"
1014
}
1115
],
1216
"require": {

src/Codeception/Specify.php

Lines changed: 39 additions & 193 deletions
Original file line numberDiff line numberDiff line change
@@ -1,224 +1,70 @@
1-
<?php
1+
<?php declare(strict_types=1);
2+
23
namespace Codeception;
34

4-
use Codeception\Specify\SpecifyTest;
5-
use Codeception\Specify\ObjectProperty;
6-
use PHPUnit\Framework\AssertionFailedError;
5+
use Closure;
6+
use Codeception\Specify\SpecifyHooks;
77
use PHPUnit\Framework\TestCase;
88

99
trait Specify
1010
{
11-
private $beforeSpecify = array();
12-
private $afterSpecify = array();
13-
14-
/**
15-
* @var \DeepCopy\DeepCopy()
16-
*/
17-
private $copier;
18-
19-
/**
20-
* @var SpecifyTest
21-
*/
22-
private $currentSpecifyTest;
23-
24-
private $specifyName = '';
25-
26-
/**
27-
* @return SpecifyTest
28-
*/
29-
public function getCurrentSpecifyTest()
30-
{
31-
return $this->currentSpecifyTest;
32-
}
33-
34-
public function should($specification, \Closure $callable = null, $params = [])
35-
{
36-
$this->specify("should " . $specification, $callable, $params);
37-
}
38-
39-
public function it($specification, \Closure $callable = null, $params = [])
40-
{
41-
$this->specify($specification, $callable, $params);
42-
}
43-
44-
public function describe($specification, \Closure $callable = null)
45-
{
46-
$this->specify($specification, $callable);
47-
}
48-
49-
public function specify($specification, \Closure $callable = null, $params = [])
50-
{
51-
if (!$callable) {
52-
return;
53-
}
54-
55-
/** @var $this TestCase **/
56-
if (!$this->copier) {
57-
$this->copier = new \DeepCopy\DeepCopy();
58-
$this->copier->skipUncloneable();
59-
}
60-
61-
$properties = $this->getSpecifyObjectProperties();
62-
63-
// prepare for execution
64-
$examples = $this->getSpecifyExamples($params);
65-
$showExamplesIndex = $examples !== [[]];
66-
67-
$specifyName = $this->specifyName;
68-
$this->specifyName .= ' ' . $specification;
69-
70-
foreach ($examples as $idx => $example) {
71-
$test = new SpecifyTest($callable->bindTo($this));
72-
$this->currentSpecifyTest = $test;
73-
$test->setName($this->getName() . ' |' . $this->specifyName);
74-
$test->setExample($example);
75-
if ($showExamplesIndex) {
76-
$test->setName($this->getName() . ' |' . $this->specifyName . ' # example ' . $idx);
77-
}
78-
79-
// copy current object properties
80-
$this->specifyCloneProperties($properties);
81-
82-
if (!empty($this->beforeSpecify) && is_array($this->beforeSpecify)) {
83-
foreach ($this->beforeSpecify as $closure) {
84-
if ($closure instanceof \Closure) $closure->__invoke();
85-
}
86-
}
87-
88-
$test->run($this->getTestResultObject());
89-
$this->specifyCheckMockObjects();
90-
91-
// restore object properties
92-
$this->specifyRestoreProperties($properties);
93-
94-
if (!empty($this->afterSpecify) && is_array($this->afterSpecify)) {
95-
foreach ($this->afterSpecify as $closure) {
96-
if ($closure instanceof \Closure) $closure->__invoke();
97-
}
98-
}
99-
}
100-
101-
// revert specify name
102-
$this->specifyName = $specifyName;
103-
}
104-
105-
/**
106-
* @param $params
107-
* @return array
108-
* @throws \RuntimeException
109-
*/
110-
private function getSpecifyExamples($params)
111-
{
112-
if (isset($params['examples'])) {
113-
if (!is_array($params['examples'])) throw new \RuntimeException("Examples should be an array");
114-
return $params['examples'];
115-
}
116-
return [[]];
11+
use SpecifyHooks {
12+
afterSpecify as public;
13+
beforeSpecify as public;
14+
cleanSpecify as public;
15+
getCurrentSpecifyTest as public;
11716
}
11817

119-
/**
120-
* @return \ReflectionClass|null
121-
*/
122-
private function specifyGetPhpUnitReflection()
18+
public function specify(string $thing, Closure $code = null, $examples = []): ?self
12319
{
124-
if ($this instanceof \PHPUnit\Framework\TestCase) {
125-
return new \ReflectionClass(\PHPUnit\Framework\TestCase::class);
20+
if ($code instanceof Closure) {
21+
$this->runSpec($thing, $code, $examples);
22+
return null;
12623
}
24+
return $this;
12725
}
12826

129-
private function specifyCheckMockObjects()
27+
public function describe(string $feature, Closure $code = null): ?self
13028
{
131-
if (($phpUnitReflection = $this->specifyGetPhpUnitReflection()) !== null) {
132-
$verifyMockObjects = $phpUnitReflection->getMethod('verifyMockObjects');
133-
$verifyMockObjects->setAccessible(true);
134-
$verifyMockObjects->invoke($this);
29+
if ($code instanceof Closure) {
30+
$this->runSpec($feature, $code);
31+
return null;
13532
}
33+
return $this;
13634
}
13735

138-
function beforeSpecify(\Closure $callable = null)
139-
{
140-
$this->beforeSpecify[] = $callable->bindTo($this);
141-
}
142-
143-
function afterSpecify(\Closure $callable = null)
144-
{
145-
$this->afterSpecify[] = $callable->bindTo($this);
146-
}
147-
148-
function cleanSpecify()
36+
public function it(string $specification, Closure $code = null, $examples = []): self
14937
{
150-
$this->beforeSpecify = $this->afterSpecify = array();
151-
}
152-
153-
/**
154-
* @param ObjectProperty[] $properties
155-
*/
156-
private function specifyRestoreProperties($properties)
157-
{
158-
foreach ($properties as $property) {
159-
$property->restoreValue();
38+
if ($code instanceof Closure) {
39+
$this->runSpec($specification, $code, $examples);
40+
return $this;
16041
}
42+
TestCase::markTestIncomplete();
43+
return $this;
16144
}
16245

163-
/**
164-
* @return ObjectProperty[]
165-
*/
166-
private function getSpecifyObjectProperties()
46+
public function its(string $specification, Closure $code = null, $examples = []): self
16747
{
168-
$objectReflection = new \ReflectionObject($this);
169-
$properties = $objectReflection->getProperties();
170-
171-
if (($classProperties = $this->specifyGetClassPrivateProperties()) !== []) {
172-
$properties = array_merge($properties, $classProperties);
173-
}
174-
175-
$clonedProperties = [];
176-
177-
foreach ($properties as $property) {
178-
/** @var $property \ReflectionProperty **/
179-
$docBlock = $property->getDocComment();
180-
if (!$docBlock) {
181-
continue;
182-
}
183-
if (preg_match('~\*(\s+)?@specify\s?~', $docBlock)) {
184-
$property->setAccessible(true);
185-
$clonedProperties[] = new ObjectProperty($this, $property);
186-
}
187-
}
188-
189-
// isolate mockObjects property from PHPUnit\Framework\TestCase
190-
if ($classReflection = $this->specifyGetPhpUnitReflection()) {
191-
$property = $classReflection->getProperty('mockObjects');
192-
// remove all mock objects inherited from parent scope(s)
193-
$clonedProperties[] = new ObjectProperty($this, $property);
194-
$property->setValue($this, []);
195-
}
196-
197-
return $clonedProperties;
48+
return $this->it($specification, $code, $examples);
19849
}
19950

200-
private function specifyGetClassPrivateProperties()
51+
public function should(string $behavior, Closure $code = null, $examples = []): self
20152
{
202-
static $properties = [];
203-
204-
if (!isset($properties[__CLASS__])) {
205-
$reflection = new \ReflectionClass(__CLASS__);
206-
207-
$properties[__CLASS__] = (get_class($this) !== __CLASS__)
208-
? $reflection->getProperties(\ReflectionProperty::IS_PRIVATE) : [];
53+
if ($code instanceof Closure) {
54+
$this->runSpec('should ' . $behavior, $code, $examples);
55+
return $this;
20956
}
210-
211-
return $properties[__CLASS__];
57+
TestCase::markTestIncomplete();
58+
return $this;
21259
}
21360

214-
/**
215-
* @param ObjectProperty[] $properties
216-
*/
217-
private function specifyCloneProperties($properties)
61+
public function shouldNot(string $behavior, Closure $code = null, $examples = []): self
21862
{
219-
foreach ($properties as $property) {
220-
$propertyValue = $property->getValue();
221-
$property->setValue($this->copier->copy($propertyValue));
63+
if ($code instanceof Closure) {
64+
$this->runSpec('should not ' . $behavior, $code, $examples);
65+
return $this;
22266
}
67+
TestCase::markTestIncomplete();
68+
return $this;
22369
}
22470
}

src/Codeception/Specify/ObjectProperty.php

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
<?php
2+
23
namespace Codeception\Specify;
34

5+
use ReflectionProperty;
6+
47
/**
58
* Helper for manipulating by an object property.
69
*
@@ -14,7 +17,7 @@ class ObjectProperty
1417
private $owner;
1518

1619
/**
17-
* @var \ReflectionProperty|string
20+
* @var ReflectionProperty|string
1821
*/
1922
private $property;
2023

@@ -35,8 +38,8 @@ public function __construct($owner, $property, $value = null)
3538
$this->owner = $owner;
3639
$this->property = $property;
3740

38-
if (!($this->property instanceof \ReflectionProperty)) {
39-
$this->property = new \ReflectionProperty($owner, $this->property);
41+
if (!($this->property instanceof ReflectionProperty)) {
42+
$this->property = new ReflectionProperty($owner, $this->property);
4043
}
4144

4245
$this->property->setAccessible(true);

0 commit comments

Comments
 (0)