1: <?php
  2:   3:   4:   5:   6:   7:   8:   9:  10:  11:  12:  13:  14:  15:  16:  17:  18: 
 19: 
 20: App::uses('Dispatcher', 'Routing');
 21: App::uses('CakeTestCase', 'TestSuite');
 22: App::uses('Router', 'Routing');
 23: App::uses('CakeRequest', 'Network');
 24: App::uses('CakeResponse', 'Network');
 25: App::uses('Helper', 'View');
 26: App::uses('CakeEvent', 'Event');
 27: 
 28:  29:  30:  31:  32: 
 33: class ControllerTestDispatcher extends Dispatcher {
 34: 
 35:  36:  37:  38:  39: 
 40:     public $testController = null;
 41: 
 42:  43:  44:  45:  46: 
 47:     public $loadRoutes = true;
 48: 
 49:  50:  51:  52:  53: 
 54:     protected function _getController($request, $response) {
 55:         if ($this->testController === null) {
 56:             $this->testController = parent::_getController($request, $response);
 57:         }
 58:         $this->testController->helpers = array_merge(array('InterceptContent'), $this->testController->helpers);
 59:         $this->testController->setRequest($request);
 60:         $this->testController->response = $this->response;
 61:         foreach ($this->testController->Components->attached() as $component) {
 62:             $object = $this->testController->Components->{$component};
 63:             if (isset($object->response)) {
 64:                 $object->response = $response;
 65:             }
 66:             if (isset($object->request)) {
 67:                 $object->request = $request;
 68:             }
 69:         }
 70:         return $this->testController;
 71:     }
 72: 
 73:  74:  75:  76:  77: 
 78:     protected function _loadRoutes() {
 79:         parent::_loadRoutes();
 80:         if (!$this->loadRoutes) {
 81:             Router::reload();
 82:         }
 83:     }
 84: 
 85: }
 86: 
 87:  88:  89:  90:  91: 
 92: class InterceptContentHelper extends Helper {
 93: 
 94:  95:  96:  97:  98: 
 99:     public function afterRender($viewFile) {
100:         $this->_View->assign('__view_no_layout__', $this->_View->fetch('content'));
101:         $this->_View->Helpers->unload('InterceptContent');
102:     }
103: 
104: }
105: 
106: 107: 108: 109: 110: 
111: abstract class ControllerTestCase extends CakeTestCase {
112: 
113: 114: 115: 116: 117: 
118:     public $controller = null;
119: 
120: 121: 122: 123: 124: 
125:     public $autoMock = true;
126: 
127: 128: 129: 130: 131: 
132:     public $loadRoutes = true;
133: 
134: 135: 136: 137: 138: 
139:     public $vars = null;
140: 
141: 142: 143: 144: 145: 
146:     public $view = null;
147: 
148: 149: 150: 151: 152: 
153:     public $contents = null;
154: 
155: 156: 157: 158: 159: 
160:     public $result = null;
161: 
162: 163: 164: 165: 166: 
167:     public $headers = null;
168: 
169: 170: 171: 172: 173: 174: 175: 
176:     protected $_dirtyController = false;
177: 
178: 179: 180: 181: 182: 183: 184: 185: 186: 
187:     public function __call($name, $arguments) {
188:         if ($name == 'testAction') {
189:             return call_user_func_array(array($this, '_testAction'), $arguments);
190:         }
191:         throw new BadMethodCallException("Method '{$name}' does not exist.");
192:     }
193: 
194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 209: 210: 211: 212: 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: 289: 290: 291: 292: 293: 294: 295: 296: 297: 298: 299: 300: 301: 302: 303: 304: 305: 306: 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:         $_controller = $this->getMock($name . 'Controller', $mocks['methods'], array(), '', false);
331:         $_controller->name = $name;
332:         $request = $this->getMock('CakeRequest');
333:         $response = $this->getMock('CakeResponse', array('_sendHeader'));
334:         $_controller->__construct($request, $response);
335: 
336:         $config = ClassRegistry::config('Model');
337:         foreach ($mocks['models'] as $model => $methods) {
338:             if (is_string($methods)) {
339:                 $model = $methods;
340:                 $methods = true;
341:             }
342:             if ($methods === true) {
343:                 $methods = array();
344:             }
345:             ClassRegistry::init($model);
346:             list($plugin, $name) = pluginSplit($model);
347:             $config = array_merge((array)$config, array('name' => $model));
348:             $_model = $this->getMock($name, $methods, array($config));
349:             ClassRegistry::removeObject($name);
350:             ClassRegistry::addObject($name, $_model);
351:         }
352: 
353:         foreach ($mocks['components'] as $component => $methods) {
354:             if (is_string($methods)) {
355:                 $component = $methods;
356:                 $methods = true;
357:             }
358:             if ($methods === true) {
359:                 $methods = array();
360:             }
361:             list($plugin, $name) = pluginSplit($component, true);
362:             $componentClass = $name . 'Component';
363:             App::uses($componentClass, $plugin . 'Controller/Component');
364:             if (!class_exists($componentClass)) {
365:                 throw new MissingComponentException(array(
366:                     'class' => $componentClass
367:                 ));
368:             }
369:             $_component = $this->getMock($componentClass, $methods, array(), '', false);
370:             $_controller->Components->set($name, $_component);
371:         }
372: 
373:         $_controller->constructClasses();
374:         $this->_dirtyController = false;
375: 
376:         $this->controller = $_controller;
377:         return $this->controller;
378:     }
379: 
380: }
381: