Subversion Repositories SmartDukaan

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
13532 anikendra 1
<?php
2
/**
3
 * ControllerTestCase file
4
 *
5
 * CakePHP(tm) Tests <http://book.cakephp.org/2.0/en/development/testing.html>
6
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
7
 *
8
 * Licensed under The MIT License
9
 * For full copyright and license information, please see the LICENSE.txt
10
 * Redistributions of files must retain the above copyright notice
11
 *
12
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
13
 * @link          http://book.cakephp.org/2.0/en/development/testing.html CakePHP(tm) Tests
14
 * @package       Cake.TestSuite
15
 * @since         CakePHP(tm) v 2.0
16
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
17
 */
18
 
19
App::uses('Dispatcher', 'Routing');
20
App::uses('CakeTestCase', 'TestSuite');
21
App::uses('Router', 'Routing');
22
App::uses('CakeRequest', 'Network');
23
App::uses('CakeResponse', 'Network');
24
App::uses('Helper', 'View');
25
App::uses('CakeEvent', 'Event');
26
 
27
/**
28
 * ControllerTestDispatcher class
29
 *
30
 * @package       Cake.TestSuite
31
 */
32
class ControllerTestDispatcher extends Dispatcher {
33
 
34
/**
35
 * The controller to use in the dispatch process
36
 *
37
 * @var Controller
38
 */
39
	public $testController = null;
40
 
41
/**
42
 * Use custom routes during tests
43
 *
44
 * @var boolean
45
 */
46
	public $loadRoutes = true;
47
 
48
/**
49
 * Returns the test controller
50
 *
51
 * @return Controller
52
 */
53
	protected function _getController($request, $response) {
54
		if ($this->testController === null) {
55
			$this->testController = parent::_getController($request, $response);
56
		}
57
		$this->testController->helpers = array_merge(array('InterceptContent'), $this->testController->helpers);
58
		$this->testController->setRequest($request);
59
		$this->testController->response = $this->response;
60
		foreach ($this->testController->Components->loaded() as $component) {
61
			$object = $this->testController->Components->{$component};
62
			if (isset($object->response)) {
63
				$object->response = $response;
64
			}
65
			if (isset($object->request)) {
66
				$object->request = $request;
67
			}
68
		}
69
		return $this->testController;
70
	}
71
 
72
/**
73
 * Loads routes and resets if the test case dictates it should
74
 *
75
 * @return void
76
 */
77
	protected function _loadRoutes() {
78
		parent::_loadRoutes();
79
		if (!$this->loadRoutes) {
80
			Router::reload();
81
		}
82
	}
83
 
84
}
85
 
86
/**
87
 * InterceptContentHelper class
88
 *
89
 * @package       Cake.TestSuite
90
 */
91
class InterceptContentHelper extends Helper {
92
 
93
/**
94
 * Intercepts and stores the contents of the view before the layout is rendered
95
 *
96
 * @param string $viewFile The view file
97
 */
98
	public function afterRender($viewFile) {
99
		$this->_View->assign('__view_no_layout__', $this->_View->fetch('content'));
100
		$this->_View->Helpers->unload('InterceptContent');
101
	}
102
 
103
}
104
 
105
/**
106
 * ControllerTestCase class
107
 *
108
 * @package       Cake.TestSuite
109
 */
110
abstract class ControllerTestCase extends CakeTestCase {
111
 
112
/**
113
 * The controller to test in testAction
114
 *
115
 * @var Controller
116
 */
117
	public $controller = null;
118
 
119
/**
120
 * Automatically mock controllers that aren't mocked
121
 *
122
 * @var boolean
123
 */
124
	public $autoMock = true;
125
 
126
/**
127
 * Use custom routes during tests
128
 *
129
 * @var boolean
130
 */
131
	public $loadRoutes = true;
132
 
133
/**
134
 * The resulting view vars of the last testAction call
135
 *
136
 * @var array
137
 */
138
	public $vars = null;
139
 
140
/**
141
 * The resulting rendered view of the last testAction call
142
 *
143
 * @var string
144
 */
145
	public $view = null;
146
 
147
/**
148
 * The resulting rendered layout+view of the last testAction call
149
 *
150
 * @var string
151
 */
152
	public $contents = null;
153
 
154
/**
155
 * The returned result of the dispatch (requestAction), if any
156
 *
157
 * @var string
158
 */
159
	public $result = null;
160
 
161
/**
162
 * The headers that would have been sent by the action
163
 *
164
 * @var string
165
 */
166
	public $headers = null;
167
 
168
/**
169
 * Flag for checking if the controller instance is dirty.
170
 * Once a test has been run on a controller it should be rebuilt
171
 * to clean up properties.
172
 *
173
 * @var boolean
174
 */
175
	protected $_dirtyController = false;
176
 
177
/**
178
 * Used to enable calling ControllerTestCase::testAction() without the testing
179
 * framework thinking that it's a test case
180
 *
181
 * @param string $name The name of the function
182
 * @param array $arguments Array of arguments
183
 * @return the return of _testAction
184
 * @throws BadMethodCallException when you call methods that don't exist.
185
 */
186
	public function __call($name, $arguments) {
187
		if ($name === 'testAction') {
188
			return call_user_func_array(array($this, '_testAction'), $arguments);
189
		}
190
		throw new BadMethodCallException("Method '{$name}' does not exist.");
191
	}
192
 
193
/**
194
 * Lets you do functional tests of a controller action.
195
 *
196
 * ### Options:
197
 *
198
 * - `data` Will be used as the request data. If the `method` is GET,
199
 *   data will be used a GET params. If the `method` is POST, it will be used
200
 *   as POST data. By setting `$options['data']` to a string, you can simulate XML or JSON
201
 *   payloads to your controllers allowing you to test REST webservices.
202
 * - `method` POST or GET. Defaults to POST.
203
 * - `return` Specify the return type you want. Choose from:
204
 *     - `vars` Get the set view variables.
205
 *     - `view` Get the rendered view, without a layout.
206
 *     - `contents` Get the rendered view including the layout.
207
 *     - `result` Get the return value of the controller action. Useful
208
 *       for testing requestAction methods.
209
 *
210
 * @param string $url The url to test
211
 * @param array $options See options
212
 * @return mixed
213
 */
214
	protected function _testAction($url = '', $options = array()) {
215
		$this->vars = $this->result = $this->view = $this->contents = $this->headers = null;
216
 
217
		$options = array_merge(array(
218
			'data' => array(),
219
			'method' => 'POST',
220
			'return' => 'result'
221
		), $options);
222
 
223
		$restore = array('get' => $_GET, 'post' => $_POST);
224
 
225
		$_SERVER['REQUEST_METHOD'] = strtoupper($options['method']);
226
		if (is_array($options['data'])) {
227
			if (strtoupper($options['method']) === 'GET') {
228
				$_GET = $options['data'];
229
				$_POST = array();
230
			} else {
231
				$_POST = $options['data'];
232
				$_GET = array();
233
			}
234
		}
235
		$request = $this->getMock('CakeRequest', array('_readInput'), array($url));
236
 
237
		if (is_string($options['data'])) {
238
			$request->expects($this->any())
239
				->method('_readInput')
240
				->will($this->returnValue($options['data']));
241
		}
242
 
243
		$Dispatch = new ControllerTestDispatcher();
244
		foreach (Router::$routes as $route) {
245
			if ($route instanceof RedirectRoute) {
246
				$route->response = $this->getMock('CakeResponse', array('send'));
247
			}
248
		}
249
		$Dispatch->loadRoutes = $this->loadRoutes;
250
		$Dispatch->parseParams(new CakeEvent('ControllerTestCase', $Dispatch, array('request' => $request)));
251
		if (!isset($request->params['controller']) && Router::currentRoute()) {
252
			$this->headers = Router::currentRoute()->response->header();
253
			return;
254
		}
255
		if ($this->_dirtyController) {
256
			$this->controller = null;
257
		}
258
 
259
		$plugin = empty($request->params['plugin']) ? '' : Inflector::camelize($request->params['plugin']) . '.';
260
		if ($this->controller === null && $this->autoMock) {
261
			$this->generate($plugin . Inflector::camelize($request->params['controller']));
262
		}
263
		$params = array();
264
		if ($options['return'] === 'result') {
265
			$params['return'] = 1;
266
			$params['bare'] = 1;
267
			$params['requested'] = 1;
268
		}
269
		$Dispatch->testController = $this->controller;
270
		$Dispatch->response = $this->getMock('CakeResponse', array('send'));
271
		$this->result = $Dispatch->dispatch($request, $Dispatch->response, $params);
272
		$this->controller = $Dispatch->testController;
273
		$this->vars = $this->controller->viewVars;
274
		$this->contents = $this->controller->response->body();
275
		if (isset($this->controller->View)) {
276
			$this->view = $this->controller->View->fetch('__view_no_layout__');
277
		}
278
		$this->_dirtyController = true;
279
		$this->headers = $Dispatch->response->header();
280
 
281
		$_GET = $restore['get'];
282
		$_POST = $restore['post'];
283
 
284
		return $this->{$options['return']};
285
	}
286
 
287
/**
288
 * Generates a mocked controller and mocks any classes passed to `$mocks`. By
289
 * default, `_stop()` is stubbed as is sending the response headers, so to not
290
 * interfere with testing.
291
 *
292
 * ### Mocks:
293
 *
294
 * - `methods` Methods to mock on the controller. `_stop()` is mocked by default
295
 * - `models` Models to mock. Models are added to the ClassRegistry so they any
296
 *   time they are instantiated the mock will be created. Pass as key value pairs
297
 *   with the value being specific methods on the model to mock. If `true` or
298
 *   no value is passed, the entire model will be mocked.
299
 * - `components` Components to mock. Components are only mocked on this controller
300
 *   and not within each other (i.e., components on components)
301
 *
302
 * @param string $controller Controller name
303
 * @param array $mocks List of classes and methods to mock
304
 * @return Controller Mocked controller
305
 * @throws MissingControllerException When controllers could not be created.
306
 * @throws MissingComponentException When components could not be created.
307
 */
308
	public function generate($controller, $mocks = array()) {
309
		list($plugin, $controller) = pluginSplit($controller);
310
		if ($plugin) {
311
			App::uses($plugin . 'AppController', $plugin . '.Controller');
312
			$plugin .= '.';
313
		}
314
		App::uses($controller . 'Controller', $plugin . 'Controller');
315
		if (!class_exists($controller . 'Controller')) {
316
			throw new MissingControllerException(array(
317
				'class' => $controller . 'Controller',
318
				'plugin' => substr($plugin, 0, -1)
319
			));
320
		}
321
		ClassRegistry::flush();
322
 
323
		$mocks = array_merge_recursive(array(
324
			'methods' => array('_stop'),
325
			'models' => array(),
326
			'components' => array()
327
		), (array)$mocks);
328
 
329
		list($plugin, $name) = pluginSplit($controller);
330
		$controllerObj = $this->getMock($name . 'Controller', $mocks['methods'], array(), '', false);
331
		$controllerObj->name = $name;
332
		$request = $this->getMock('CakeRequest');
333
		$response = $this->getMock('CakeResponse', array('_sendHeader'));
334
		$controllerObj->__construct($request, $response);
335
		$controllerObj->Components->setController($controllerObj);
336
 
337
		$config = ClassRegistry::config('Model');
338
		foreach ($mocks['models'] as $model => $methods) {
339
			if (is_string($methods)) {
340
				$model = $methods;
341
				$methods = true;
342
			}
343
			if ($methods === true) {
344
				$methods = array();
345
			}
346
			$this->getMockForModel($model, $methods, $config);
347
		}
348
 
349
		foreach ($mocks['components'] as $component => $methods) {
350
			if (is_string($methods)) {
351
				$component = $methods;
352
				$methods = true;
353
			}
354
			if ($methods === true) {
355
				$methods = array();
356
			}
357
			list($plugin, $name) = pluginSplit($component, true);
358
			$componentClass = $name . 'Component';
359
			App::uses($componentClass, $plugin . 'Controller/Component');
360
			if (!class_exists($componentClass)) {
361
				throw new MissingComponentException(array(
362
					'class' => $componentClass
363
				));
364
			}
365
			$config = isset($controllerObj->components[$component]) ? $controllerObj->components[$component] : array();
366
			$componentObj = $this->getMock($componentClass, $methods, array($controllerObj->Components, $config));
367
			$controllerObj->Components->set($name, $componentObj);
368
			$controllerObj->Components->enable($name);
369
		}
370
 
371
		$controllerObj->constructClasses();
372
		$this->_dirtyController = false;
373
 
374
		$this->controller = $controllerObj;
375
		return $this->controller;
376
	}
377
 
378
}