Completed
Pull Request — master (#88)
by Kenji
02:55
created

CIPHPUnitTestCase::warningOff()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 6
rs 9.4286
cc 1
eloc 3
nc 1
nop 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
/**
12
 * @property CIPHPUnitTestRequest    $request
13
 * @property CIPHPUnitTestDouble     $double
14
 * @property CIPHPUnitTestReflection $reflection
15
 */
16
class CIPHPUnitTestCase extends PHPUnit_Framework_TestCase
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
17
{
18
	protected $_error_reporting = -1;
19
	
20
	/**
21
	 * @var CI_Controller CodeIgniter instance
22
	 */
23
	protected $CI;
24
	
25
	protected $class_map = [
26
		'request'    => 'CIPHPUnitTestRequest',
27
		'double'     => 'CIPHPUnitTestDouble',
28
		'reflection' => 'CIPHPUnitTestReflection',
29
	];
30
31
	public function setCI(CI_Controller $CI)
32
	{
33
		$this->CI = $CI;
34
	}
35
36
	public function __get($name)
37
	{
38
		if (isset($this->class_map[$name]))
39
		{
40
			$this->$name = new $this->class_map[$name]($this);
41
			return $this->$name;
42
		}
43
44
		throw new LogicException('No such property: ' . $name);
45
	}
46
47
	public static function setUpBeforeClass()
0 ignored issues
show
Coding Style introduced by
setUpBeforeClass uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
48
	{
49
		// Fix CLI args, because you may set invalid URI characters
50
		// For example, when you run tests on NetBeans
51
		$_SERVER['argv'] = [
52
			'index.php',
53
		];
54
		$_SERVER['argc'] = 1;
55
	}
56
57
	/**
58
	 * Reset CodeIgniter instance and assign new CodeIgniter instance as $this->CI
59
	 */
60
	public function resetInstance()
61
	{
62
		reset_instance();
63
		new CI_Controller();
64
		$this->CI =& get_instance();
65
	}
66
67
	protected function tearDown()
68
	{
69
		if (class_exists('MonkeyPatch', false))
70
		{
71
			if (MonkeyPatchManager::isEnabled('FunctionPatcher'))
72
			{
73
				try {
74
					MonkeyPatch::verifyFunctionInvocations();
75
				} catch (Exception $e) {
76
					MonkeyPatch::resetFunctions();
77
					throw $e;
78
				}
79
80
				MonkeyPatch::resetFunctions();
81
			}
82
83
			if (MonkeyPatchManager::isEnabled('MethodPatcher'))
84
			{
85
				try {
86
					MonkeyPatch::verifyMethodInvocations();
87
				} catch (Exception $e) {
88
					MonkeyPatch::resetMethods();
89
					throw $e;
90
				}
91
92
				MonkeyPatch::resetMethods();
93
			}
94
		}
95
	}
96
97
	/**
98
	 * Request to Controller
99
	 *
100
	 * @param string       $http_method HTTP method
101
	 * @param array|string $argv        array of controller,method,arg|uri
102
	 * @param array        $params      POST parameters/Query string
103
	 */
104
	public function request($http_method, $argv, $params = [])
105
	{
106
		return $this->request->request($http_method, $argv, $params);
107
	}
108
109
	/**
110
	 * Request to Controller using ajax request
111
	 *
112
	 * @param string       $http_method HTTP method
113
	 * @param array|string $argv        array of controller,method,arg|uri
114
	 * @param array        $params      POST parameters/Query string
115
	 */
116
	public function ajaxRequest($http_method, $argv, $params = [])
0 ignored issues
show
Coding Style introduced by
ajaxRequest uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
117
	{
118
		$_SERVER['HTTP_X_REQUESTED_WITH'] = 'xmlhttprequest';
119
		return $this->request($http_method, $argv, $params);
120
	}
121
122
	/**
123
	 * Get Mock Object
124
	 *
125
	 * $email = $this->getMockBuilder('CI_Email')
126
	 *	->setMethods(['send'])
127
	 *	->getMock();
128
	 * $email->method('send')->willReturn(TRUE);
129
	 *
130
	 *  will be
131
	 *
132
	 * $email = $this->getDouble('CI_Email', ['send' => TRUE]);
133
	 *
134
	 * @param  string $classname
135
	 * @param  array  $params             [method_name => return_value]
136
	 * @param  bool   $enable_constructor enable constructor or not
137
	 * @return object PHPUnit mock object
138
	 */
139
	public function getDouble($classname, $params, $enable_constructor = false)
140
	{
141
		return $this->double->getDouble($classname, $params, $enable_constructor);
142
	}
143
144
	/**
145
	 * Verifies that method was called exactly $times times
146
	 *
147
	 * $loader->expects($this->exactly(2))
148
	 * 	->method('view')
149
	 * 	->withConsecutive(
150
	 *		['shop_confirm', $this->anything(), TRUE],
151
	 * 		['shop_tmpl_checkout', $this->anything()]
152
	 * 	);
153
	 *
154
	 *  will be
155
	 *
156
	 * $this->verifyInvokedMultipleTimes(
157
	 * 	$loader,
158
	 * 	'view',
159
	 * 	2,
160
	 * 	[
161
	 * 		['shop_confirm', $this->anything(), TRUE],
162
	 * 		['shop_tmpl_checkout', $this->anything()]
163
	 * 	]
164
	 * );
165
	 *
166
	 * @param object $mock   PHPUnit mock object
167
	 * @param string $method
168
	 * @param int    $times
169
	 * @param array  $params arguments
170
	 */
171
	public function verifyInvokedMultipleTimes($mock, $method, $times, $params = null)
172
	{
173
		$this->double->verifyInvokedMultipleTimes(
174
			$mock, $method, $times, $params
175
		);
176
	}
177
178
	/**
179
	 * Verifies a method was invoked at least once
180
	 *
181
	 * @param object $mock   PHPUnit mock object
182
	 * @param string $method
183
	 * @param array  $params arguments
184
	 */
185
	public function verifyInvoked($mock, $method, $params = null)
186
	{
187
		$this->double->verifyInvoked($mock, $method, $params);
188
	}
189
190
	/**
191
	 * Verifies that method was invoked only once
192
	 *
193
	 * @param object $mock   PHPUnit mock object
194
	 * @param string $method
195
	 * @param array  $params arguments
196
	 */
197
	public function verifyInvokedOnce($mock, $method, $params = null)
198
	{
199
		$this->double->verifyInvokedOnce($mock, $method, $params);
200
	}
201
202
	/**
203
	 * Verifies that method was not called
204
	 *
205
	 * @param object $mock   PHPUnit mock object
206
	 * @param string $method
207
	 * @param array  $params arguments
208
	 */
209
	public function verifyNeverInvoked($mock, $method, $params = null)
210
	{
211
		$this->double->verifyNeverInvoked($mock, $method, $params);
212
	}
213
214
	public function warningOff()
215
	{
216
		$this->_error_reporting = error_reporting(
217
			E_ALL & ~E_WARNING & ~E_NOTICE
218
		);
219
	}
220
221
	public function warningOn()
222
	{
223
		error_reporting($this->_error_reporting);
224
	}
225
226
	/**
227
	 * Asserts HTTP response code
228
	 * 
229
	 * @param int $code
230
	 */
231
	public function assertResponseCode($code)
232
	{
233
		$status = $this->request->getStatus();
234
		$actual = $status['code'];
235
236
		$this->assertSame(
237
			$code,
238
			$actual,
239
			'Status code is not ' . $code . ' but ' . $actual . '.'
240
		);
241
	}
242
243
	/**
244
	 * Asserts HTTP response header
245
	 * 
246
	 * @param string $name  header name
247
	 * @param string $value header value
248
	 */
249
	public function assertResponseHeader($name, $value)
250
	{
251
		$CI =& get_instance();
252
		$actual = $CI->output->get_header($name);
253
254
		if ($actual === null)
255
		{
256
			$this->fail("The '$name' header is not set.\nNote that `assertResponseHeader()` can only assert headers set by `\$this->output->set_header()`");
257
		}
258
259
		$this->assertEquals(
260
			$value,
261
			$actual,
262
			"The '$name' header is not '$value' but '$actual'."
263
		);
264
	}
265
266
	/**
267
	 * Asserts HTTP response cookie
268
	 * 
269
	 * @param string       $name            cookie name
270
	 * @param string|array $value           cookie value|array of cookie params
271
	 * @param bool         $allow_duplicate whether to allow duplicated cookies
272
	 */
273
	public function assertResponseCookie($name, $value, $allow_duplicate = false)
274
	{
275
		$CI =& get_instance();
276
		$cookies = isset($CI->output->_cookies[$name])
277
			? $CI->output->_cookies[$name] : null;
278
279
		if ($cookies === null)
280
		{
281
			$this->fail("The cookie '$name' is not set.\nNote that `assertResponseCookie()` can only assert cookies set by `\$this->input->set_cookie()`");
282
		}
283
284
		$count = count($cookies);
285
		if ($count > 1 && ! $allow_duplicate)
286
		{
287
			$values = [];
288
			foreach ($cookies as $key => $val)
289
			{
290
				$values[] = "'{$val['value']}'";
291
			}
292
			$values = implode(' and ', $values);
293
			$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()`");
294
		}
295
296
		// Get the last cookie
297
		$cookie = $cookies[$count - 1];
298
		if (is_string($value))
299
		{
300
			$this->assertEquals(
301
				$value,
302
				$cookie['value'],
303
				"The cookie '$name' value is not '$value' but '{$cookie['value']}'."
304
			);
305
			return;
306
		}
307
308
		foreach ($value as $key => $val)
309
		{
310
			$this->assertEquals(
311
				$value[$key],
312
				$cookie[$key],
313
				"The cookie '$name' $key is not '{$value[$key]}' but '{$cookie[$key]}'."
314
			);
315
		}
316
	}
317
318
	/**
319
	 * Asserts Redirect
320
	 * 
321
	 * @param string $uri  URI to redirect
322
	 * @param int    $code response code
323
	 */
324
	public function assertRedirect($uri, $code = null)
325
	{
326
		$status = $this->request->getStatus();
327
328
		if ($status['redirect'] === null)
329
		{
330
			$this->fail('redirect() is not called.');
331
		}
332
333
		if (! function_exists('site_url'))
334
		{
335
			$CI =& get_instance();
336
			$CI->load->helper('url');
337
		}
338
		$absolute_url = site_url($uri);
339
		$expected = 'Redirect to ' . $absolute_url;
340
341
		$this->assertSame(
342
			$expected,
343
			$status['redirect'],
344
			'URL to redirect is not ' . $expected . ' but ' . $status['redirect'] . '.'
345
		);
346
347
		if ($code !== null)
348
		{
349
			$this->assertSame(
350
				$code,
351
				$status['code'],
352
				'Status code is not ' . $code . ' but ' . $status['code'] . '.'
353
			);
354
		}
355
	}
356
}
357