/
TestCase.php
299 lines (262 loc) · 7.62 KB
/
TestCase.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
<?php
namespace Concise;
use Concise\Mock\MockBuilder;
use Concise\Services\AssertionBuilder;
use Concise\Syntax\MatcherParser;
use Concise\Mock\MockManager;
use Concise\Validation\ArgumentChecker;
use Exception;
use PHPUnit_Framework_AssertionFailedError;
use PHPUnit_Framework_TestCase;
use ReflectionClass;
use Concise\Mock\MockInterface;
// Load the keyword cache before the test suite begins.
Keywords::load();
class TestCase extends PHPUnit_Framework_TestCase
{
/**
* Used as a placeholder for with() clauses where the parameter is unrestrictive. For the
* curious, this is the SHA1('a') with an extra 'a' on the end.
*/
const ANYTHING = '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8a';
/**
* @var MockManager
*/
protected $mockManager;
/**
* @var array
*/
protected $properties = array();
/**
* @var array
*/
protected $verifyFailures = array();
/**
* @param string|null $name
* @param array $data
* @param string $dataName
*/
public function __construct($name = null, array $data = array(), $dataName = '')
{
parent::__construct($name, $data, $dataName);
$this->mockManager = new MockManager($this);
}
/**
* @return MockManager
*/
public function getMockManager()
{
return $this->mockManager;
}
/**
* @return MatcherParser
*/
protected function getMatcherParserInstance()
{
return MatcherParser::getInstance();
}
/**
* @param string $name
* @throws Exception
* @return mixed
*/
public function __get($name)
{
if (!array_key_exists($name, $this->properties)) {
throw new Exception("No such attribute '{$name}'.");
}
return $this->properties[$name];
}
public function __isset($name)
{
return array_key_exists($name, $this->properties);
}
public function __unset($name)
{
unset($this->properties[$name]);
}
/**
* @param string $name
* @param mixed $value
* @throws Exception
*/
public function __set($name, $value)
{
$parser = $this->getMatcherParserInstance();
if (in_array($name, $parser->getKeywords())) {
throw new Exception("You cannot assign an attribute with the keyword '$name'.");
}
$this->properties[$name] = $value;
}
/**
* @return array
*/
public function getData()
{
return $this->properties + get_object_vars($this);
}
/**
* @return string
*/
protected function getRealTestName()
{
$name = substr($this->getName(), 20);
$pos = strpos($name, ':');
return substr($name, 0, $pos);
}
/**
* These attributes are provided by the base PHPUnit classes.
* @return array
*/
public static function getPHPUnitProperties()
{
return array(
'backupGlobals' => null,
'backupGlobalsBlacklist' => array(),
'backupStaticAttributes' => null,
'backupStaticAttributesBlacklist' => array(),
'runTestInSeparateProcess' => null,
'preserveGlobalState' => true,
);
}
protected function createAssertion(array $args)
{
if (count($args) > 1 || is_bool($args[0])) {
$builder = new AssertionBuilder($args);
return $builder->getAssertion();
}
return $this->getMatcherParserInstance()->compile($args[0], $this->getData());
}
/**
* @return boolean
*/
public function assert()
{
$assertion = $this->createAssertion(func_get_args());
if ($this instanceof TestCase) {
$assertion->setTestCase($this);
}
return $assertion->run();
}
/**
* @return void
*/
public function tearDown()
{
$this->mockManager->validateMocks();
if ($this->verifyFailures) {
$count = count($this->verifyFailures);
$message = "$count verify failure" . ($count === 1 ? '' : 's') . ":";
$message .= "\n\n" . implode("\n\n", $this->verifyFailures);
throw new PHPUnit_Framework_AssertionFailedError($message);
}
parent::tearDown();
}
/**
* @param string $className
* @param array $constructorArgs
* @return MockBuilder
*/
public function mock($className = '\stdClass', array $constructorArgs = array())
{
return new MockBuilder($this, $className, false, $constructorArgs);
}
/**
* @param string $className
* @param array $constructorArgs
* @return MockBuilder
*/
public function niceMock($className = '\stdClass', array $constructorArgs = array())
{
return new MockBuilder($this, $className, true, $constructorArgs);
}
/**
* @param object $instance
* @return MockBuilder
*/
public function partialMock($instance)
{
ArgumentChecker::check($instance, 'object');
$mockBuilder = new MockBuilder($this, get_class($instance), true, array());
$mockBuilder->disableConstructor();
$mockBuilder->setObjectState($instance);
return $mockBuilder;
}
protected function loadKeywords()
{
$parser = MatcherParser::getInstance();
$all = array();
foreach ($parser->getAllMatcherDescriptions() as $syntax => $description) {
$simpleSyntax = preg_replace('/\\?(:[a-zA-Z0-9-,]+)/', '?', $syntax);
foreach (explode('?', $simpleSyntax) as $part) {
$p = trim($part);
$all[str_replace(' ', '_', $p)] = $p;
}
}
foreach ($all as $name => $value) {
if (!defined($name)) {
define($name, $value);
}
}
define('on_error', 'on error');
}
public function setUp()
{
parent::setUp();
$this->verifyFailures = array();
}
/**
* @param MockBuilder $mockBuilder
* @param object $mockInstance
*/
public function addMockInstance(MockBuilder $mockBuilder, $mockInstance)
{
$this->mockManager->addMockInstance($mockBuilder, $mockInstance);
}
protected function getReflectionProperty($object, $property)
{
$className = get_class($object);
if ($object instanceof MockInterface) {
$className = get_parent_class($object);
if (!$className) {
$message = "You cannot set a property on an interface (" . get_class($object) . ").";
throw new Exception($message);
}
}
$reflection = new ReflectionClass($className);
$property = $reflection->getProperty($property);
$property->setAccessible(true);
return $property;
}
public function getProperty($object, $property)
{
$property = $this->getReflectionProperty($object, $property);
return $property->getValue($object);
}
public function setProperty($object, $property, $value)
{
$property = $this->getReflectionProperty($object, $property);
$property->setValue($object, $value);
}
/**
* Validate a mock now.
* @param MockInterface $mock The mock instance to verify.
* @return bool
*/
public function assertMock(MockInterface $mock)
{
$this->mockManager->validateMockByInstance($mock);
return true;
}
/**
* @return boolean|null
*/
public function verify()
{
try {
call_user_func_array(array($this, 'assert'), func_get_args());
} catch (PHPUnit_Framework_AssertionFailedError $e) {
$this->verifyFailures[] = $e->getMessage();
}
}
}