1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 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: 29: 30: 31:
32: class ControllerTestDispatcher extends Dispatcher {
33:
34: 35: 36: 37: 38:
39: public $testController = null;
40:
41: 42: 43: 44: 45:
46: public $loadRoutes = true;
47:
48: 49: 50: 51: 52: 53: 54:
55: protected function _getController($request, $response) {
56: if ($this->testController === null) {
57: $this->testController = parent::_getController($request, $response);
58: }
59: $this->testController->helpers = array_merge(array('InterceptContent'), $this->testController->helpers);
60: $this->testController->setRequest($request);
61: $this->testController->response = $this->response;
62: foreach ($this->testController->Components->loaded() as $component) {
63: $object = $this->testController->Components->{$component};
64: if (isset($object->response)) {
65: $object->response = $response;
66: }
67: if (isset($object->request)) {
68: $object->request = $request;
69: }
70: }
71: return $this->testController;
72: }
73:
74: 75: 76: 77: 78:
79: protected function _loadRoutes() {
80: parent::_loadRoutes();
81: if (!$this->loadRoutes) {
82: Router::reload();
83: }
84: }
85:
86: }
87:
88: 89: 90: 91: 92:
93: class InterceptContentHelper extends Helper {
94:
95: 96: 97: 98: 99: 100:
101: public function afterRender($viewFile) {
102: $this->_View->assign('__view_no_layout__', $this->_View->fetch('content'));
103: $this->_View->Helpers->unload('InterceptContent');
104: }
105:
106: }
107:
108: 109: 110: 111: 112: 113:
114: abstract class ControllerTestCase extends CakeTestCase {
115:
116: 117: 118: 119: 120:
121: public $controller = null;
122:
123: 124: 125: 126: 127:
128: public $autoMock = true;
129:
130: 131: 132: 133: 134:
135: public $loadRoutes = true;
136:
137: 138: 139: 140: 141:
142: public $vars = null;
143:
144: 145: 146: 147: 148:
149: public $view = null;
150:
151: 152: 153: 154: 155:
156: public $contents = null;
157:
158: 159: 160: 161: 162:
163: public $result = null;
164:
165: 166: 167: 168: 169:
170: public $headers = null;
171:
172: 173: 174: 175: 176: 177: 178:
179: protected $_dirtyController = false;
180:
181: 182: 183: 184: 185:
186: protected $_responseClass = 'CakeResponse';
187:
188: 189: 190: 191: 192: 193: 194: 195: 196:
197: public function __call($name, $arguments) {
198: if ($name === 'testAction') {
199: return call_user_func_array(array($this, '_testAction'), $arguments);
200: }
201: throw new BadMethodCallException("Method '{$name}' does not exist.");
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: protected function _testAction($url, $options = array()) {
227: $this->vars = $this->result = $this->view = $this->contents = $this->headers = null;
228:
229: $options += array(
230: 'data' => array(),
231: 'method' => 'POST',
232: 'return' => 'result'
233: );
234:
235: if (is_array($url)) {
236: $url = Router::url($url);
237: }
238:
239: $restore = array('get' => $_GET, 'post' => $_POST);
240:
241: $_SERVER['REQUEST_METHOD'] = strtoupper($options['method']);
242: if (is_array($options['data'])) {
243: if (strtoupper($options['method']) === 'GET') {
244: $_GET = $options['data'];
245: $_POST = array();
246: } else {
247: $_POST = $options['data'];
248: $_GET = array();
249: }
250: }
251:
252: if (strpos($url, '?') !== false) {
253: list($url, $query) = explode('?', $url, 2);
254: parse_str($query, $queryArgs);
255: $_GET += $queryArgs;
256: }
257:
258: $_SERVER['REQUEST_URI'] = $url;
259:
260: $request = $this->getMock('CakeRequest', array('_readInput'));
261:
262: if (is_string($options['data'])) {
263: $request->expects($this->any())
264: ->method('_readInput')
265: ->will($this->returnValue($options['data']));
266: }
267:
268: $Dispatch = $this->_createDispatcher();
269: foreach (Router::$routes as $route) {
270: if ($route instanceof RedirectRoute) {
271: $route->response = $this->getMock('CakeResponse', array('send'));
272: }
273: }
274: $Dispatch->loadRoutes = $this->loadRoutes;
275: $Dispatch->parseParams(new CakeEvent('ControllerTestCase', $Dispatch, array('request' => $request)));
276: if (!isset($request->params['controller']) && Router::currentRoute()) {
277: $this->headers = Router::currentRoute()->response->header();
278: return null;
279: }
280: if ($this->_dirtyController) {
281: $this->controller = null;
282: }
283:
284: $plugin = empty($request->params['plugin']) ? '' : Inflector::camelize($request->params['plugin']) . '.';
285: if ($this->controller === null && $this->autoMock) {
286: $this->generate($plugin . Inflector::camelize($request->params['controller']));
287: }
288: $params = array();
289: if ($options['return'] === 'result') {
290: $params['return'] = 1;
291: $params['bare'] = 1;
292: $params['requested'] = 1;
293: }
294: $Dispatch->testController = $this->controller;
295: $Dispatch->response = $this->getMock($this->_responseClass, array('send', '_clearBuffer'));
296: $this->result = $Dispatch->dispatch($request, $Dispatch->response, $params);
297:
298:
299: while (Router::getRequest()) {
300: Router::popRequest();
301: }
302:
303: $this->controller = $Dispatch->testController;
304: $this->vars = $this->controller->viewVars;
305: $this->contents = $this->controller->response->body();
306: if (isset($this->controller->View)) {
307: $this->view = $this->controller->View->fetch('__view_no_layout__');
308: }
309: $this->_dirtyController = true;
310: $this->headers = $Dispatch->response->header();
311:
312: $_GET = $restore['get'];
313: $_POST = $restore['post'];
314:
315: return $this->{$options['return']};
316: }
317:
318: 319: 320: 321: 322:
323: protected function _createDispatcher() {
324: return new ControllerTestDispatcher();
325: }
326:
327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345: 346: 347:
348: public function generate($controller, $mocks = array()) {
349: list($plugin, $controller) = pluginSplit($controller);
350: if ($plugin) {
351: App::uses($plugin . 'AppController', $plugin . '.Controller');
352: $plugin .= '.';
353: }
354: App::uses($controller . 'Controller', $plugin . 'Controller');
355: if (!class_exists($controller . 'Controller')) {
356: throw new MissingControllerException(array(
357: 'class' => $controller . 'Controller',
358: 'plugin' => substr($plugin, 0, -1)
359: ));
360: }
361: ClassRegistry::flush();
362:
363: $mocks = array_merge_recursive(array(
364: 'methods' => array('_stop'),
365: 'models' => array(),
366: 'components' => array()
367: ), (array)$mocks);
368:
369: list($plugin, $name) = pluginSplit($controller);
370:
371: $controllerObj = $this->getMock($name . 'Controller', $mocks['methods'], array(), '', false);
372: $controllerObj->name = $name;
373:
374: $request = $this->getMock('CakeRequest');
375:
376: $response = $this->getMock($this->_responseClass, array('_sendHeader'));
377: $controllerObj->__construct($request, $response);
378: $controllerObj->Components->setController($controllerObj);
379:
380: $config = ClassRegistry::config('Model');
381: foreach ($mocks['models'] as $model => $methods) {
382: if (is_string($methods)) {
383: $model = $methods;
384: $methods = true;
385: }
386: if ($methods === true) {
387: $methods = array();
388: }
389: $this->getMockForModel($model, $methods, $config);
390: }
391:
392: foreach ($mocks['components'] as $component => $methods) {
393: if (is_string($methods)) {
394: $component = $methods;
395: $methods = true;
396: }
397: if ($methods === true) {
398: $methods = array();
399: }
400: $config = isset($controllerObj->components[$component]) ? $controllerObj->components[$component] : array();
401: if (isset($config['className'])) {
402: $alias = $component;
403: $component = $config['className'];
404: }
405: list($plugin, $name) = pluginSplit($component, true);
406: if (!isset($alias)) {
407: $alias = $name;
408: }
409: $componentClass = $name . 'Component';
410: App::uses($componentClass, $plugin . 'Controller/Component');
411: if (!class_exists($componentClass)) {
412: throw new MissingComponentException(array(
413: 'class' => $componentClass
414: ));
415: }
416:
417: $componentObj = $this->getMock($componentClass, $methods, array($controllerObj->Components, $config));
418: $controllerObj->Components->set($alias, $componentObj);
419: $controllerObj->Components->enable($alias);
420: unset($alias);
421: }
422:
423: $controllerObj->constructClasses();
424: $this->_dirtyController = false;
425:
426: $this->controller = $controllerObj;
427: return $this->controller;
428: }
429:
430: 431: 432: 433: 434:
435: public function tearDown() {
436: parent::tearDown();
437: unset(
438: $this->contents,
439: $this->controller,
440: $this->headers,
441: $this->result,
442: $this->view,
443: $this->vars
444: );
445: }
446: }
447: