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 = new ControllerTestDispatcher();
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: 324: 325: 326: 327: 328: 329: 330: 331: 332: 333: 334: 335: 336: 337: 338: 
339:     public function generate($controller, $mocks = array()) {
340:         list($plugin, $controller) = pluginSplit($controller);
341:         if ($plugin) {
342:             App::uses($plugin . 'AppController', $plugin . '.Controller');
343:             $plugin .= '.';
344:         }
345:         App::uses($controller . 'Controller', $plugin . 'Controller');
346:         if (!class_exists($controller . 'Controller')) {
347:             throw new MissingControllerException(array(
348:                 'class' => $controller . 'Controller',
349:                 'plugin' => substr($plugin, 0, -1)
350:             ));
351:         }
352:         ClassRegistry::flush();
353: 
354:         $mocks = array_merge_recursive(array(
355:             'methods' => array('_stop'),
356:             'models' => array(),
357:             'components' => array()
358:         ), (array)$mocks);
359: 
360:         list($plugin, $name) = pluginSplit($controller);
361:         
362:         $controllerObj = $this->getMock($name . 'Controller', $mocks['methods'], array(), '', false);
363:         $controllerObj->name = $name;
364:         
365:         $request = $this->getMock('CakeRequest');
366:         
367:         $response = $this->getMock($this->_responseClass, array('_sendHeader'));
368:         $controllerObj->__construct($request, $response);
369:         $controllerObj->Components->setController($controllerObj);
370: 
371:         $config = ClassRegistry::config('Model');
372:         foreach ($mocks['models'] as $model => $methods) {
373:             if (is_string($methods)) {
374:                 $model = $methods;
375:                 $methods = true;
376:             }
377:             if ($methods === true) {
378:                 $methods = array();
379:             }
380:             $this->getMockForModel($model, $methods, $config);
381:         }
382: 
383:         foreach ($mocks['components'] as $component => $methods) {
384:             if (is_string($methods)) {
385:                 $component = $methods;
386:                 $methods = true;
387:             }
388:             if ($methods === true) {
389:                 $methods = array();
390:             }
391:             list($plugin, $name) = pluginSplit($component, true);
392:             $componentClass = $name . 'Component';
393:             App::uses($componentClass, $plugin . 'Controller/Component');
394:             if (!class_exists($componentClass)) {
395:                 throw new MissingComponentException(array(
396:                     'class' => $componentClass
397:                 ));
398:             }
399:             $config = isset($controllerObj->components[$component]) ? $controllerObj->components[$component] : array();
400:             
401:             $componentObj = $this->getMock($componentClass, $methods, array($controllerObj->Components, $config));
402:             $controllerObj->Components->set($name, $componentObj);
403:             $controllerObj->Components->enable($name);
404:         }
405: 
406:         $controllerObj->constructClasses();
407:         $this->_dirtyController = false;
408: 
409:         $this->controller = $controllerObj;
410:         return $this->controller;
411:     }
412: 
413: }
414: