33
44use Codeception \Specify \Config ;
55use 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}
0 commit comments