3
3
4
4
use Codeception \Specify \Config ;
5
5
use Codeception \Specify \ConfigBuilder ;
6
+ use Codeception \Specify \ObjectProperty ;
6
7
7
- trait Specify {
8
+ trait Specify
9
+ {
8
10
9
11
private $ beforeSpecify = array ();
10
12
private $ afterSpecify = array ();
@@ -27,23 +29,29 @@ private function specifyInit()
27
29
if (!$ this ->specifyConfig ) $ this ->specifyConfig = Config::create ();
28
30
}
29
31
30
- function specify ($ specification , \Closure $ callable = null , $ params = [])
31
- {
32
+ function specify ($ specification , \Closure $ callable = null , $ params = [])
33
+ {
32
34
if (!$ callable ) return ;
33
35
$ this ->specifyInit ();
34
36
35
37
$ test = $ callable ->bindTo ($ this );
36
- $ name = $ this ->getName ();
37
- $ this ->setName ($ this ->getName ().' | ' .$ specification );
38
+ $ oldName = $ this ->getName ();
39
+ $ newName = $ oldName . ' | ' . $ specification ;
40
+
41
+ $ this ->setName ($ newName );
38
42
39
- $ properties = get_object_vars ( $ this );
43
+ $ properties = $ this -> getSpecifyObjectProperties ( );
40
44
41
45
// prepare for execution
42
46
$ throws = $ this ->getSpecifyExpectedException ($ params );
43
47
$ examples = $ this ->getSpecifyExamples ($ params );
48
+ $ showExamplesIndex = $ examples !== [[]];
44
49
45
50
foreach ($ examples as $ idx => $ example ) {
46
- $ this ->setName ($ name .' | ' .$ specification .' | examples index ' . $ idx );
51
+ if ($ showExamplesIndex ) {
52
+ $ this ->setName ($ newName . ' | examples index ' . $ idx );
53
+ }
54
+
47
55
// copy current object properties
48
56
$ this ->specifyCloneProperties ($ properties );
49
57
@@ -52,13 +60,12 @@ function specify($specification, \Closure $callable = null, $params = [])
52
60
if ($ closure instanceof \Closure) $ closure ->__invoke ();
53
61
}
54
62
}
63
+
55
64
$ this ->specifyExecute ($ test , $ throws , $ example );
56
65
57
66
// restore object properties
58
- foreach ($ properties as $ property => $ val ) {
59
- if ($ this ->specifyConfig ->propertyIgnored ($ property )) continue ;
60
- $ this ->$ property = $ val ;
61
- }
67
+ $ this ->specifyRestoreProperties ($ properties );
68
+
62
69
if (!empty ($ this ->afterSpecify ) && is_array ($ this ->afterSpecify )) {
63
70
foreach ($ this ->afterSpecify as $ closure ) {
64
71
if ($ closure instanceof \Closure) $ closure ->__invoke ();
@@ -67,8 +74,8 @@ function specify($specification, \Closure $callable = null, $params = [])
67
74
}
68
75
69
76
// restore test name
70
- $ this ->setName ($ name );
71
- }
77
+ $ this ->setName ($ oldName );
78
+ }
72
79
73
80
/**
74
81
* @param $params
@@ -114,8 +121,10 @@ private function specifyExecute($test, $throws = false, $examples = array())
114
121
}
115
122
116
123
$ result = $ this ->getTestResultObject ();
124
+
117
125
try {
118
126
call_user_func_array ($ test , $ examples );
127
+ $ this ->specifyCheckMockObjects ();
119
128
} catch (\PHPUnit_Framework_AssertionFailedError $ e ) {
120
129
if ($ throws !== get_class ($ e )){
121
130
$ result ->addFailure (clone ($ this ), $ e , $ result ->time ());
@@ -173,29 +182,107 @@ function cleanSpecify()
173
182
}
174
183
175
184
/**
176
- * @param $properties
177
- * @return array
185
+ * @param ObjectProperty[] $properties
178
186
*/
179
187
private function specifyCloneProperties ($ properties )
180
188
{
181
- foreach ($ properties as $ property => $ val ) {
182
- if ( $ this -> specifyConfig -> propertyIgnored ( $ property )) {
183
- continue ;
184
- }
185
- 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 )) {
186
194
continue ;
187
195
}
188
196
189
- if ($ this ->specifyConfig ->propertyIsShallowCloned ($ property )) {
190
- if (is_object ($ val )) {
191
- $ this -> $ property = clone $ val ;
197
+ if ($ this ->specifyConfig ->propertyIsShallowCloned ($ propertyName )) {
198
+ if (is_object ($ propertyValue )) {
199
+ $ property -> setValue ( clone $ propertyValue ) ;
192
200
} else {
193
- $ this -> $ property = $ val ;
201
+ $ property -> setValue ( $ propertyValue ) ;
194
202
}
195
203
}
196
- if ($ this ->specifyConfig ->propertyIsDeeplyCloned ($ property )) {
197
- $ this ->$ property = $ this ->copier ->copy ($ val );
204
+
205
+ if ($ this ->specifyConfig ->propertyIsDeeplyCloned ($ propertyName )) {
206
+ $ property ->setValue ($ this ->copier ->copy ($ propertyValue ));
207
+ }
208
+ }
209
+ }
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 ;
198
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 ' );
199
286
}
200
287
}
201
288
}
0 commit comments