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: 
 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:     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->attached() as $component) {
 61:             $object = $this->testController->Components->{$component};
 62:             if (isset($object->response)) {
 63:                 $object->response = $response;
 64:             }
 65:         }
 66:         if (isset($object->request)) {
 67:             $object->request = $request;
 68:         }
 69:         return $this->testController;
 70:     }
 71: 
 72:  73:  74:  75:  76: 
 77:     protected function _loadRoutes() {
 78:         parent::_loadRoutes();
 79:         if (!$this->loadRoutes) {
 80:             Router::reload();
 81:         }
 82:     }
 83: }
 84: 
 85:  86:  87:  88:  89: 
 90: class InterceptContentHelper extends Helper {
 91: 
 92:  93:  94:  95:  96: 
 97:     public function afterRender($viewFile) {
 98:         $this->_View->_viewNoLayout = $this->_View->output;
 99:         $this->_View->Helpers->unload('InterceptContent');
100:     }
101: }
102: 
103: 104: 105: 106: 107: 
108: abstract class ControllerTestCase extends CakeTestCase {
109: 
110: 111: 112: 113: 114: 
115:     public $controller = null;
116: 
117: 118: 119: 120: 121: 
122:     public $autoMock = true;
123: 
124: 125: 126: 127: 128: 
129:     public $loadRoutes = true;
130: 
131: 132: 133: 134: 135: 
136:     public $vars = null;
137: 
138: 139: 140: 141: 142: 
143:     public $view = null;
144: 
145: 146: 147: 148: 149: 
150:     public $contents = null;
151: 
152: 153: 154: 155: 156: 
157:     public $result = null;
158: 
159: 160: 161: 162: 163: 
164:     public $headers = null;
165: 
166: 167: 168: 169: 170: 171: 172: 
173:     private $__dirtyController = false;
174: 
175: 176: 177: 178: 179: 180: 181: 182: 
183:     public function __call($name, $arguments) {
184:         if ($name == 'testAction') {
185:             return call_user_func_array(array($this, '_testAction'), $arguments);
186:         }
187:     }
188: 
189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205: 206: 207: 208: 
209:     protected function _testAction($url = '', $options = array()) {
210:         $this->vars = $this->result = $this->view = $this->contents = $this->headers = null;
211: 
212:         $options = array_merge(array(
213:             'data' => array(),
214:             'method' => 'POST',
215:             'return' => 'result'
216:         ), $options);
217: 
218:         $_SERVER['REQUEST_METHOD'] = strtoupper($options['method']);
219:         if (is_array($options['data'])) {
220:             if (strtoupper($options['method']) == 'GET') {
221:                 $_GET = $options['data'];
222:                 $_POST = array();
223:             } else {
224:                 $_POST = $options['data'];
225:                 $_GET = array();
226:             }
227:         }
228:         $request = $this->getMock('CakeRequest', array('_readInput'), array($url));
229: 
230:         if (is_string($options['data'])) {
231:             $request->expects($this->any())
232:                 ->method('_readInput')
233:                 ->will($this->returnValue($options['data']));
234:         }
235: 
236:         $Dispatch = new ControllerTestDispatcher();
237:         foreach (Router::$routes as $route) {
238:             if ($route instanceof RedirectRoute) {
239:                 $route->response = $this->getMock('CakeResponse', array('send'));
240:             }
241:         }
242:         $Dispatch->loadRoutes = $this->loadRoutes;
243:         $request = $Dispatch->parseParams($request);
244:         if (!isset($request->params['controller'])) {
245:             $this->headers = Router::currentRoute()->response->header();
246:             return;
247:         }
248:         if ($this->__dirtyController) {
249:             $this->controller = null;
250:         }
251: 
252:         $plugin = empty($request->params['plugin']) ? '' : Inflector::camelize($request->params['plugin']) . '.';
253:         if ($this->controller === null && $this->autoMock) {
254:             $this->generate($plugin . Inflector::camelize($request->params['controller']));
255:         }
256:         $params = array();
257:         if ($options['return'] == 'result') {
258:             $params['return'] = 1;
259:             $params['bare'] = 1;
260:             $params['requested'] = 1;
261:         }
262:         $Dispatch->testController = $this->controller;
263:         $Dispatch->response = $this->getMock('CakeResponse', array('send'));
264:         $this->result = $Dispatch->dispatch($request, $Dispatch->response, $params);
265:         $this->controller = $Dispatch->testController;
266:         $this->vars = $this->controller->viewVars;
267:         $this->contents = $this->controller->response->body();
268:         if (isset($this->controller->View)) {
269:             $this->view = $this->controller->View->_viewNoLayout;
270:         }
271:         $this->__dirtyController = true;
272:         $this->headers = $Dispatch->response->header();
273:         return $this->{$options['return']};
274:     }
275: 
276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292: 293: 294: 
295:     public function generate($controller, $mocks = array()) {
296:         list($plugin, $controller) = pluginSplit($controller);
297:         if ($plugin) {
298:             App::uses($plugin . 'AppController', $plugin . '.Controller');
299:             $plugin .= '.';
300:         }
301:         App::uses($controller . 'Controller', $plugin . 'Controller');
302:         if (!class_exists($controller.'Controller')) {
303:             throw new MissingControllerException(array(
304:                 'class' => $controller . 'Controller',
305:                 'plugin' => substr($plugin, 0, -1)
306:             ));
307:         }
308:         ClassRegistry::flush();
309: 
310:         $mocks = array_merge_recursive(array(
311:             'methods' => array('_stop'),
312:             'models' => array(),
313:             'components' => array()
314:         ), (array)$mocks);
315: 
316:         list($plugin, $name) = pluginSplit($controller);
317:         $_controller = $this->getMock($name.'Controller', $mocks['methods'], array(), '', false);
318:         $_controller->name = $name;
319:         $request = $this->getMock('CakeRequest');
320:         $response = $this->getMock('CakeResponse', array('_sendHeader'));
321:         $_controller->__construct($request, $response);
322: 
323:         $config = ClassRegistry::config('Model');
324:         foreach ($mocks['models'] as $model => $methods) {
325:             if (is_string($methods)) {
326:                 $model = $methods;
327:                 $methods = true;
328:             }
329:             if ($methods === true) {
330:                 $methods = array();
331:             }
332:             ClassRegistry::init($model);
333:             list($plugin, $name) = pluginSplit($model);
334:             $config = array_merge((array)$config, array('name' => $model));
335:             $_model = $this->getMock($name, $methods, array($config));
336:             ClassRegistry::removeObject($name);
337:             ClassRegistry::addObject($name, $_model);
338:         }
339: 
340:         foreach ($mocks['components'] as $component => $methods) {
341:             if (is_string($methods)) {
342:                 $component = $methods;
343:                 $methods = true;
344:             }
345:             if ($methods === true) {
346:                 $methods = array();
347:             }
348:             list($plugin, $name) = pluginSplit($component, true);
349:             $componentClass = $name . 'Component';
350:             App::uses($componentClass, $plugin . 'Controller/Component');
351:             if (!class_exists($componentClass)) {
352:                 throw new MissingComponentException(array(
353:                     'class' => $componentClass
354:                 ));
355:             }
356:             $_component = $this->getMock($componentClass, $methods, array(), '', false);
357:             $_controller->Components->set($name, $_component);
358:         }
359: 
360:         $_controller->constructClasses();
361:         $this->__dirtyController = false;
362: 
363:         $this->controller = $_controller;
364:         return $this->controller;
365:     }
366: }
367: