Skip to content

Commit 21b586f

Browse files
authored
Merge pull request #37 from ischenko/fix-unknown-property
Fix for issue: Property $beforeSpecify does not exist
2 parents 000a41e + 2615d16 commit 21b586f

File tree

8 files changed

+307
-28
lines changed

8 files changed

+307
-28
lines changed

phpunit.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
<phpunit colors="true">
1+
<phpunit colors="true"
2+
bootstrap="tests/_bootstrap.php">
23
<testsuites>
34
<testsuite name="Specify">
45
<directory>tests</directory>

src/Codeception/Specify.php

Lines changed: 101 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33

44
use Codeception\Specify\Config;
55
use Codeception\Specify\ConfigBuilder;
6+
use Codeception\Specify\ObjectProperty;
67

7-
trait Specify {
8+
trait Specify
9+
{
810

911
private $beforeSpecify = array();
1012
private $afterSpecify = array();
@@ -27,7 +29,7 @@ private function specifyInit()
2729
if (!$this->specifyConfig) $this->specifyConfig = Config::create();
2830
}
2931

30-
function specify($specification, \Closure $callable = null, $params = [])
32+
function specify($specification, \Closure $callable = null, $params = [])
3133
{
3234
if (!$callable) return;
3335
$this->specifyInit();
@@ -38,7 +40,7 @@ function specify($specification, \Closure $callable = null, $params = [])
3840

3941
$this->setName($newName);
4042

41-
$properties = get_object_vars($this);
43+
$properties = $this->getSpecifyObjectProperties();
4244

4345
// prepare for execution
4446
$throws = $this->getSpecifyExpectedException($params);
@@ -58,13 +60,12 @@ function specify($specification, \Closure $callable = null, $params = [])
5860
if ($closure instanceof \Closure) $closure->__invoke();
5961
}
6062
}
63+
6164
$this->specifyExecute($test, $throws, $example);
6265

6366
// restore object properties
64-
foreach ($properties as $property => $val) {
65-
if ($this->specifyConfig->propertyIgnored($property)) continue;
66-
$this->$property = $val;
67-
}
67+
$this->specifyRestoreProperties($properties);
68+
6869
if (!empty($this->afterSpecify) && is_array($this->afterSpecify)) {
6970
foreach ($this->afterSpecify as $closure) {
7071
if ($closure instanceof \Closure) $closure->__invoke();
@@ -120,8 +121,10 @@ private function specifyExecute($test, $throws = false, $examples = array())
120121
}
121122

122123
$result = $this->getTestResultObject();
124+
123125
try {
124126
call_user_func_array($test, $examples);
127+
$this->specifyCheckMockObjects();
125128
} catch (\PHPUnit_Framework_AssertionFailedError $e) {
126129
if ($throws !== get_class($e)){
127130
$result->addFailure(clone($this), $e, $result->time());
@@ -179,29 +182,107 @@ function cleanSpecify()
179182
}
180183

181184
/**
182-
* @param $properties
183-
* @return array
185+
* @param ObjectProperty[] $properties
184186
*/
185187
private function specifyCloneProperties($properties)
186188
{
187-
foreach ($properties as $property => $val) {
188-
if ($this->specifyConfig->propertyIgnored($property)) {
189-
continue;
190-
}
191-
if ($this->specifyConfig->classIgnored($val)) {
189+
foreach ($properties as $property) {
190+
$propertyName = $property->getName();
191+
$propertyValue = $property->getValue();
192+
193+
if ($this->specifyConfig->classIgnored($propertyValue)) {
192194
continue;
193195
}
194196

195-
if ($this->specifyConfig->propertyIsShallowCloned($property)) {
196-
if (is_object($val)) {
197-
$this->$property = clone $val;
197+
if ($this->specifyConfig->propertyIsShallowCloned($propertyName)) {
198+
if (is_object($propertyValue)) {
199+
$property->setValue(clone $propertyValue);
198200
} else {
199-
$this->$property = $val;
201+
$property->setValue($propertyValue);
200202
}
201203
}
202-
if ($this->specifyConfig->propertyIsDeeplyCloned($property)) {
203-
$this->$property = $this->copier->copy($val);
204+
205+
if ($this->specifyConfig->propertyIsDeeplyCloned($propertyName)) {
206+
$property->setValue($this->copier->copy($propertyValue));
204207
}
205208
}
206209
}
210+
211+
/**
212+
* @param ObjectProperty[] $properties
213+
*/
214+
private function specifyRestoreProperties($properties)
215+
{
216+
foreach ($properties as $property) {
217+
$property->restoreValue();
218+
}
219+
}
220+
221+
/**
222+
* @return ObjectProperty[]
223+
*/
224+
private function getSpecifyObjectProperties()
225+
{
226+
$objectReflection = new \ReflectionObject($this);
227+
$propertiesToClone = $objectReflection->getProperties();
228+
229+
if (($classProperties = $this->specifyGetClassPrivateProperties()) !== []) {
230+
$propertiesToClone = array_merge($propertiesToClone, $classProperties);
231+
}
232+
233+
$properties = [];
234+
235+
foreach ($propertiesToClone as $property) {
236+
if ($this->specifyConfig->propertyIgnored($property->getName())) {
237+
continue;
238+
}
239+
240+
$properties[] = new ObjectProperty($this, $property);
241+
}
242+
243+
// isolate mockObjects property from PHPUnit_Framework_TestCase
244+
if (($phpUnitReflection = $this->specifyGetPhpUnitReflection()) !== null) {
245+
$properties[] = $mockObjects = new ObjectProperty(
246+
$this, $phpUnitReflection->getProperty('mockObjects')
247+
);
248+
249+
// remove all mock objects inherited from parent scope(s)
250+
$mockObjects->setValue([]);
251+
}
252+
253+
return $properties;
254+
}
255+
256+
private function specifyCheckMockObjects()
257+
{
258+
if (($phpUnitReflection = $this->specifyGetPhpUnitReflection()) !== null) {
259+
$verifyMockObjects = $phpUnitReflection->getMethod('verifyMockObjects');
260+
$verifyMockObjects->setAccessible(true);
261+
$verifyMockObjects->invoke($this);
262+
}
263+
}
264+
265+
private function specifyGetClassPrivateProperties()
266+
{
267+
static $properties = [];
268+
269+
if (!isset($properties[__CLASS__])) {
270+
$reflection = new \ReflectionClass(__CLASS__);
271+
272+
$properties[__CLASS__] = (get_class($this) !== __CLASS__)
273+
? $reflection->getProperties(\ReflectionProperty::IS_PRIVATE) : [];
274+
}
275+
276+
return $properties[__CLASS__];
277+
}
278+
279+
/**
280+
* @return \ReflectionClass|null
281+
*/
282+
private function specifyGetPhpUnitReflection()
283+
{
284+
if ($this instanceof \PHPUnit_Framework_TestCase) {
285+
return new \ReflectionClass('\PHPUnit_Framework_TestCase');
286+
}
287+
}
207288
}
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
<?php
2+
namespace Codeception\Specify;
3+
4+
/**
5+
* Helper for manipulating by an object property.
6+
*
7+
* @author Roman Ishchenko <[email protected]>
8+
*/
9+
class ObjectProperty
10+
{
11+
/**
12+
* @var mixed
13+
*/
14+
private $_owner;
15+
16+
/**
17+
* @var \ReflectionProperty|string
18+
*/
19+
private $_property;
20+
21+
/**
22+
* @var mixed
23+
*/
24+
private $_initValue;
25+
26+
/**
27+
* ObjectProperty constructor.
28+
*
29+
* @param $owner
30+
* @param $property
31+
* @param $value
32+
*/
33+
public function __construct($owner, $property, $value = null)
34+
{
35+
$this->_owner = $owner;
36+
$this->_property = $property;
37+
38+
if (!($this->_property instanceof \ReflectionProperty)) {
39+
$this->_property = new \ReflectionProperty($owner, $this->_property);
40+
}
41+
42+
$this->_property->setAccessible(true);
43+
44+
$this->_initValue = ($value === null ? $this->getValue() : $value);
45+
}
46+
47+
/**
48+
* @return string
49+
*/
50+
public function getName()
51+
{
52+
return $this->_property->getName();
53+
}
54+
55+
/**
56+
* Restores initial value
57+
*/
58+
public function restoreValue()
59+
{
60+
$this->setValue($this->_initValue);
61+
}
62+
63+
/**
64+
* @return mixed
65+
*/
66+
public function getValue()
67+
{
68+
return $this->_property->getValue($this->_owner);
69+
}
70+
71+
/**
72+
* @param mixed $value
73+
*/
74+
public function setValue($value)
75+
{
76+
$this->_property->setValue($this->_owner, $value);
77+
}
78+
}

tests/ConfigTest.php

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<?php
2-
require_once __DIR__.'/../vendor/autoload.php';
32

4-
class ConfigTest extends \PHPUnit_Framework_TestCase
3+
class ConfigTest extends \SpecifyUnitTest
54
{
65
/**
76
* @var \Codeception\Specify\Config

tests/ObjectPropertyTest.php

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
3+
class ObjectPropertyTest extends \SpecifyUnitTest
4+
{
5+
private $private = 'private';
6+
7+
public function testConstruction()
8+
{
9+
$this->prop = 'test';
10+
11+
$prop = new \Codeception\Specify\ObjectProperty($this, 'prop');
12+
13+
$this->assertEquals('prop', $prop->getName());
14+
$this->assertEquals('test', $prop->getValue());
15+
16+
$prop = new \Codeception\Specify\ObjectProperty($this, 'private');
17+
18+
$this->assertEquals('private', $prop->getName());
19+
$this->assertEquals('private', $prop->getValue());
20+
21+
$prop = new \Codeception\Specify\ObjectProperty(
22+
$this, new ReflectionProperty($this, 'private')
23+
);
24+
25+
$this->assertEquals('private', $prop->getName());
26+
$this->assertEquals('private', $prop->getValue());
27+
}
28+
29+
public function testRestore()
30+
{
31+
$this->prop = 'test';
32+
33+
$prop = new \Codeception\Specify\ObjectProperty($this, 'prop');
34+
$prop->setValue('another value');
35+
36+
$this->assertEquals('another value', $this->prop);
37+
38+
$prop->restoreValue();
39+
40+
$this->assertEquals('test', $this->prop);
41+
42+
$prop = new \Codeception\Specify\ObjectProperty($this, 'private');
43+
$prop->setValue('another private value');
44+
45+
$this->assertEquals('another private value', $this->private);
46+
47+
$prop->restoreValue();
48+
49+
$this->assertEquals('private', $this->private);
50+
51+
$prop = new \Codeception\Specify\ObjectProperty($this, 'prop', 'testing');
52+
53+
$this->assertEquals('test', $prop->getValue());
54+
55+
$prop->setValue('Hello, World!');
56+
57+
$this->assertEquals($prop->getValue(), $this->prop);
58+
$this->assertEquals('Hello, World!', $prop->getValue());
59+
60+
$prop->restoreValue();
61+
62+
$this->assertEquals($prop->getValue(), $this->prop);
63+
$this->assertEquals('testing', $prop->getValue());
64+
}
65+
}

0 commit comments

Comments
 (0)