Completed
Push — master ( e38a14...2bd32e )
by Kenji
11s
created

CIPHPUnitTestCase::tearDownAfterClass()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * Part of ci-phpunit-test
4
 *
5
 * @author     Kenji Suzuki <https://github.com/kenjis>
6
 * @license    MIT License
7
 * @copyright  2015 Kenji Suzuki
8
 * @link       https://github.com/kenjis/ci-phpunit-test
9
 */
10
11
// Support PHPUnit 6.0
12
if (! class_exists('PHPUnit_Framework_TestCase'))
13
{
14
	class_alias('PHPUnit\Framework\TestCase', 'PHPUnit_Framework_TestCase');
15
}
16
17
/**
18
 * @property CIPHPUnitTestRequest    $request
19
 * @property CIPHPUnitTestDouble     $double
20
 * @property CIPHPUnitTestReflection $reflection
21
 */
22
class CIPHPUnitTestCase extends PHPUnit_Framework_TestCase
23
{
24
	protected $_error_reporting = -1;
25
26
	/**
27
	 * If you have a route with closure, PHPUnit can't serialize global variables.
28
	 * You would see `Exception: Serialization of 'Closure' is not allowed`.
29
	 *
30
	 * @var array
31
	 */
32
	protected $backupGlobalsBlacklist = ['RTR'];
33
34
	/**
35
	 * Detect warnings and notices in a request output
36
	 *
37
	 * @var bool
38
	 */
39
	protected $strictRequestErrorCheck = true;
40
41
	protected $restoreErrorHandler = false;
42
43
	/**
44
	 * @var CI_Controller CodeIgniter instance
45
	 */
46
	protected $CI;
47
48
	protected $class_map = [
49
		'request'    => 'CIPHPUnitTestRequest',
50
		'double'     => 'CIPHPUnitTestDouble',
51
		'reflection' => 'CIPHPUnitTestReflection',
52
	];
53
54
	public function setCI(CI_Controller $CI)
55
	{
56
		$this->CI = $CI;
57
	}
58
59
	public function getStrictRequestErrorCheck()
60
	{
61
		return $this->strictRequestErrorCheck;
62
	}
63
64
	public function __get($name)
65
	{
66
		if (isset($this->class_map[$name]))
67
		{
68
			$this->$name = new $this->class_map[$name]($this);
69
			return $this->$name;
70
		}
71
72
		throw new LogicException('No such property: ' . $name);
73
	}
74
75
	public static function setUpBeforeClass()
76
	{
77
		// Fix CLI args, because you may set invalid URI characters
78
		// For example, when you run tests on NetBeans
79
		$_SERVER['argv'] = [
80
			'index.php',
81
		];
82
		$_SERVER['argc'] = 1;
83
84
		// Reset current directroy
85
		chdir(FCPATH);
86
	}
87
88
	public static function tearDownAfterClass()
89
	{
90
		CIPHPUnitTestDbConnectionStore::destory();
91
	}
92
93
	/**
94
	 * Reset CodeIgniter instance and assign new CodeIgniter instance as $this->CI
95
	 */
96
	public function resetInstance()
97
	{
98
		reset_instance();
99
		CIPHPUnitTest::createCodeIgniterInstance();
100
		$this->CI =& get_instance();
101
	}
102
103
	protected function tearDown()
104
	{
105
		$this->disableStrictErrorCheck();
106
107
		if (class_exists('MonkeyPatch', false))
108
		{
109
			if (MonkeyPatchManager::isEnabled('FunctionPatcher'))
110
			{
111
				try {
112
					MonkeyPatch::verifyFunctionInvocations();
113
				} catch (Exception $e) {
114
					MonkeyPatch::resetFunctions();
115
					throw $e;
116
				}
117
118
				MonkeyPatch::resetFunctions();
119
			}
120
121
			if (MonkeyPatchManager::isEnabled('ConstantPatcher'))
122
			{
123
				MonkeyPatch::resetConstants();
124
			}
125
126
			if (MonkeyPatchManager::isEnabled('MethodPatcher'))
127
			{
128
				try {
129
					MonkeyPatch::verifyMethodInvocations();
130
				} catch (Exception $e) {
131
					MonkeyPatch::resetMethods();
132
					throw $e;
133
				}
134
135
				MonkeyPatch::resetMethods();
136
			}
137
		}
138
	}
139
140
	/**
141
	 * Request to Controller
142
	 *
143
	 * @param string       $http_method HTTP method
144
	 * @param array|string $argv        array of controller,method,arg|uri
145
	 * @param array        $params      POST parameters/Query string
146
	 */
147
	public function request($http_method, $argv, $params = [])
148
	{
149
		return $this->request->request($http_method, $argv, $params);
150
	}
151
152
	/**
153
	 * Disable strict error check
154
	 */
155
	public function disableStrictErrorCheck()
156
	{
157
		if ($this->restoreErrorHandler) {
158
			restore_error_handler();
159
			$this->restoreErrorHandler = false;
160
		}
161
	}
162
163
	/**
164
	 * Enable strict error check
165
	 */
166
	public function enableStrictErrorCheck()
167
	{
168
		if ($this->restoreErrorHandler) {
169
			throw new LogicException('Already strict error check mode');
170
		}
171
172
		set_error_handler(
173
			function ($errno, $errstr, $errfile, $errline) {
174
				throw new RuntimeException($errstr . ' on line ' . $errline . ' in file ' . $errfile);
175
			}
176
		);
177
178
		$this->restoreErrorHandler = true;
179
	}
180
181
	/**
182
	 * Request to Controller using ajax request
183
	 *
184
	 * @param string       $http_method HTTP method
185
	 * @param array|string $argv        array of controller,method,arg|uri
186
	 * @param array        $params      POST parameters/Query string
187
	 */
188
	public function ajaxRequest($http_method, $argv, $params = [])
189
	{
190
		$_SERVER['HTTP_X_REQUESTED_WITH'] = 'xmlhttprequest';
191
		return $this->request($http_method, $argv, $params);
192
	}
193
194
	/**
195
	 * Get Mock Object
196
	 *
197
	 * $email = $this->getMockBuilder('CI_Email')
198
	 *	->setMethods(['send'])
199
	 *	->getMock();
200
	 * $email->method('send')->willReturn(TRUE);
201
	 *
202
	 *  will be
203
	 *
204
	 * $email = $this->getDouble('CI_Email', ['send' => TRUE]);
205
	 *
206
	 * @param  string $classname
207
	 * @param  array  $params             [method_name => return_value]
208
	 * @param  bool   $enable_constructor enable constructor or not
209
	 * @return object PHPUnit mock object
210
	 */
211
	public function getDouble($classname, $params, $enable_constructor = false)
212
	{
213
		return $this->double->getDouble($classname, $params, $enable_constructor);
214
	}
215
216
	/**
217
	 * Verifies that method was called exactly $times times
218
	 *
219
	 * $loader->expects($this->exactly(2))
220
	 * 	->method('view')
221
	 * 	->withConsecutive(
222
	 *		['shop_confirm', $this->anything(), TRUE],
223
	 * 		['shop_tmpl_checkout', $this->anything()]
224
	 * 	);
225
	 *
226
	 *  will be
227
	 *
228
	 * $this->verifyInvokedMultipleTimes(
229
	 * 	$loader,
230
	 * 	'view',
231
	 * 	2,
232
	 * 	[
233
	 * 		['shop_confirm', $this->anything(), TRUE],
234
	 * 		['shop_tmpl_checkout', $this->anything()]
235
	 * 	]
236
	 * );
237
	 *
238
	 * @param object $mock   PHPUnit mock object
239
	 * @param string $method
240
	 * @param int    $times
241
	 * @param array  $params arguments
242
	 */
243
	public function verifyInvokedMultipleTimes($mock, $method, $times, $params = null)
244
	{
245
		$this->double->verifyInvokedMultipleTimes(
246
			$mock, $method, $times, $params
247
		);
248
	}
249
250
	/**
251
	 * Verifies a method was invoked at least once
252
	 *
253
	 * @param object $mock   PHPUnit mock object
254
	 * @param string $method
255
	 * @param array  $params arguments
256
	 */
257
	public function verifyInvoked($mock, $method, $params = null)
258
	{
259
		$this->double->verifyInvoked($mock, $method, $params);
260
	}
261
262
	/**
263
	 * Verifies that method was invoked only once
264
	 *
265
	 * @param object $mock   PHPUnit mock object
266
	 * @param string $method
267
	 * @param array  $params arguments
268
	 */
269
	public function verifyInvokedOnce($mock, $method, $params = null)
270
	{
271
		$this->double->verifyInvokedOnce($mock, $method, $params);
272
	}
273
274
	/**
275
	 * Verifies that method was not called
276
	 *
277
	 * @param object $mock   PHPUnit mock object
278
	 * @param string $method
279
	 * @param array  $params arguments
280
	 */
281
	public function verifyNeverInvoked($mock, $method, $params = null)
282
	{
283
		$this->double->verifyNeverInvoked($mock, $method, $params);
284
	}
285
286
	public function warningOff()
287
	{
288
		$this->_error_reporting = error_reporting(
289
			E_ALL & ~E_WARNING & ~E_NOTICE
290
		);
291
	}
292
293
	public function warningOn()
294
	{
295
		error_reporting($this->_error_reporting);
296
	}
297
298
	/**
299
	 * Asserts HTTP response code
300
	 *
301
	 * @param int $code
302
	 */
303
	public function assertResponseCode($code)
304
	{
305
		$status = $this->request->getStatus();
306
		$actual = $status['code'];
307
308
		$this->assertSame(
309
			$code,
310
			$actual,
311
			'Status code is not ' . $code . ' but ' . $actual . '.'
312
		);
313
	}
314
315
	/**
316
	 * Asserts HTTP response header
317
	 *
318
	 * @param string $name  header name
319
	 * @param string $value header value
320
	 */
321
	public function assertResponseHeader($name, $value)
322
	{
323
		$CI =& get_instance();
324
		$actual = $CI->output->get_header($name);
325
326
		if ($actual === null)
327
		{
328
			$this->fail("The '$name' header is not set.\nNote that `assertResponseHeader()` can only assert headers set by `\$this->output->set_header()`");
329
		}
330
331
		$this->assertEquals(
332
			$value,
333
			$actual,
334
			"The '$name' header is not '$value' but '$actual'."
335
		);
336
	}
337
338
	/**
339
	 * Asserts HTTP response cookie
340
	 *
341
	 * @param string       $name            cookie name
342
	 * @param string|array $value           cookie value|array of cookie params
343
	 * @param bool         $allow_duplicate whether to allow duplicated cookies
344
	 */
345
	public function assertResponseCookie($name, $value, $allow_duplicate = false)
346
	{
347
		$CI =& get_instance();
348
		$cookies = isset($CI->output->_cookies[$name])
349
			? $CI->output->_cookies[$name] : null;
350
351
		if ($cookies === null)
352
		{
353
			$this->fail("The cookie '$name' is not set.\nNote that `assertResponseCookie()` can only assert cookies set by `\$this->input->set_cookie()`");
354
		}
355
356
		$count = count($cookies);
357
		if ($count > 1 && ! $allow_duplicate)
358
		{
359
			$values = [];
360
			foreach ($cookies as $key => $val)
361
			{
362
				$values[] = "'{$val['value']}'";
363
			}
364
			$values = implode(' and ', $values);
365
			$this->fail("You have more than one cookie '$name'. The values are $values.\nIf it is okay, please set `true` as the 3rd argument of `assertResponseCookie()`");
366
		}
367
368
		// Get the last cookie
369
		$cookie = $cookies[$count - 1];
370
		if (is_string($value))
371
		{
372
			$this->assertEquals(
373
				$value,
374
				$cookie['value'],
375
				"The cookie '$name' value is not '$value' but '{$cookie['value']}'."
376
			);
377
			return;
378
		}
379
380
		// In case of $this->anything()
381
		if (
382
			$value instanceof PHPUnit_Framework_Constraint_IsAnything
0 ignored issues
show
Bug introduced by
The class PHPUnit_Framework_Constraint_IsAnything does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
383
			|| $value instanceof PHPUnit\Framework\Constraint\IsAnything
0 ignored issues
show
Bug introduced by
The class PHPUnit\Framework\Constraint\IsAnything does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
384
		)
385
		{
386
			$this->assertTrue(true);
387
			return;
388
		}
389
390
		foreach ($value as $key => $val)
391
		{
392
			$this->assertEquals(
393
				$value[$key],
394
				$cookie[$key],
395
				"The cookie '$name' $key is not '{$value[$key]}' but '{$cookie[$key]}'."
396
			);
397
		}
398
	}
399
400
	/**
401
	 * Asserts Redirect
402
	 *
403
	 * @param string $uri  URI to redirect
404
	 * @param int    $code response code
405
	 */
406
	public function assertRedirect($uri, $code = null)
407
	{
408
		$status = $this->request->getStatus();
409
410
		if ($status['redirect'] === null)
411
		{
412
			$this->fail('redirect() is not called.');
413
		}
414
415
		if (! function_exists('site_url'))
416
		{
417
			$CI =& get_instance();
418
			$CI->load->helper('url');
419
		}
420
421
		if (! preg_match('#^(\w+:)?//#i', $uri))
422
		{
423
			$uri = site_url($uri);
424
		}
425
		$absolute_url = $uri;
426
		$expected = 'Redirect to ' . $absolute_url;
427
428
		$this->assertSame(
429
			$expected,
430
			$status['redirect'],
431
			'URL to redirect is not ' . $expected . ' but ' . $status['redirect'] . '.'
432
		);
433
434
		if ($code !== null)
435
		{
436
			$this->assertSame(
437
				$code,
438
				$status['code'],
439
				'Status code is not ' . $code . ' but ' . $status['code'] . '.'
440
			);
441
		}
442
	}
443
}
444