CakePHP
  • Documentation
    • Book
    • API
    • Videos
    • Reporting Security Issues
    • Privacy Policy
    • Logos & Trademarks
  • Business Solutions
  • Swag
  • Road Trip
  • Team
  • Community
    • Community
    • Get Involved
    • Issues (GitHub)
    • Bakery
    • Featured Resources
    • Training
    • Meetups
    • My CakePHP
    • CakeFest
    • Newsletter
    • Linkedin
    • YouTube
    • Facebook
    • Twitter
    • Mastodon
    • Help & Support
    • Forum
    • Stack Overflow
    • Slack
    • Paid Support
CakePHP

C CakePHP 2.0 API

  • Overview
  • Tree
  • Deprecated
  • Version:
    • 2.0
      • 4.2
      • 4.1
      • 4.0
      • 3.9
      • 3.8
      • 3.7
      • 3.6
      • 3.5
      • 3.4
      • 3.3
      • 3.2
      • 3.1
      • 3.0
      • 2.10
      • 2.9
      • 2.8
      • 2.7
      • 2.6
      • 2.5
      • 2.4
      • 2.3
      • 2.2
      • 2.1
      • 2.0
      • 1.3
      • 1.2

Packages

  • Cake
    • Cache
      • Engine
    • Configure
    • Console
      • Command
        • Task
    • Controller
      • Component
        • Auth
    • Core
    • Error
    • I18n
    • Log
      • Engine
    • Model
      • Behavior
      • Datasource
        • Database
        • Session
    • Network
      • Email
      • Http
    • Routing
      • Route
    • TestSuite
      • Coverage
      • Fixture
      • Reporter
    • Utility
    • View
      • Helper

Classes

  • BakeTask
  • ControllerTask
  • DbConfigTask
  • ExtractTask
  • FixtureTask
  • ModelTask
  • PluginTask
  • ProjectTask
  • TemplateTask
  • TestTask
  • ViewTask
  1: <?php
  2: /**
  3:  * The TestTask handles creating and updating test files.
  4:  *
  5:  * PHP 5
  6:  *
  7:  * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  8:  * Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
  9:  *
 10:  * Licensed under The MIT License
 11:  * Redistributions of files must retain the above copyright notice.
 12:  *
 13:  * @copyright     Copyright 2005-2011, Cake Software Foundation, Inc. (http://cakefoundation.org)
 14:  * @link          http://cakephp.org CakePHP(tm) Project
 15:  * @since         CakePHP(tm) v 1.3
 16:  * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 17:  */
 18: 
 19: App::uses('AppShell', 'Console/Command');
 20: App::uses('BakeTask', 'Console/Command/Task');
 21: App::uses('ClassRegistry', 'Utility');
 22: 
 23: /**
 24:  * Task class for creating and updating test files.
 25:  *
 26:  * @package       Cake.Console.Command.Task
 27:  */
 28: class TestTask extends BakeTask {
 29: 
 30: /**
 31:  * path to TESTS directory
 32:  *
 33:  * @var string
 34:  */
 35:     public $path = TESTS;
 36: 
 37: /**
 38:  * Tasks used.
 39:  *
 40:  * @var array
 41:  */
 42:     public $tasks = array('Template');
 43: 
 44: /**
 45:  * class types that methods can be generated for
 46:  *
 47:  * @var array
 48:  */
 49:     public $classTypes =  array(
 50:         'Model' => 'Model',
 51:         'Controller' => 'Controller',
 52:         'Component' => 'Controller/Component',
 53:         'Behavior' => 'Model/Behavior',
 54:         'Helper' => 'View/Helper'
 55:     );
 56: 
 57: /**
 58:  * Internal list of fixtures that have been added so far.
 59:  *
 60:  * @var array
 61:  */
 62:     protected $_fixtures = array();
 63: 
 64: /**
 65:  * Execution method always used for tasks
 66:  *
 67:  * @return void
 68:  */
 69:     public function execute() {
 70:         parent::execute();
 71:         if (empty($this->args)) {
 72:             $this->_interactive();
 73:         }
 74: 
 75:         if (count($this->args) == 1) {
 76:             $this->_interactive($this->args[0]);
 77:         }
 78: 
 79:         if (count($this->args) > 1) {
 80:             $type = Inflector::underscore($this->args[0]);
 81:             if ($this->bake($type, $this->args[1])) {
 82:                 $this->out('<success>Done</success>');
 83:             }
 84:         }
 85:     }
 86: 
 87: /**
 88:  * Handles interactive baking
 89:  *
 90:  * @param string $type
 91:  * @return string|boolean
 92:  */
 93:     protected function _interactive($type = null) {
 94:         $this->interactive = true;
 95:         $this->hr();
 96:         $this->out(__d('cake_console', 'Bake Tests'));
 97:         $this->out(__d('cake_console', 'Path: %s', $this->getPath()));
 98:         $this->hr();
 99: 
100:         if ($type) {
101:             $type = Inflector::camelize($type);
102:             if (!isset($this->classTypes[$type])) {
103:                 $this->error(__d('cake_console', 'Incorrect type provided. Please choose one of %s', implode(', ', array_keys($this->classTypes))));
104:             }
105:         } else {
106:             $type = $this->getObjectType();
107:         }
108:         $className = $this->getClassName($type);
109:         return $this->bake($type, $className);
110:     }
111: 
112: /**
113:  * Completes final steps for generating data to create test case.
114:  *
115:  * @param string $type Type of object to bake test case for ie. Model, Controller
116:  * @param string $className the 'cake name' for the class ie. Posts for the PostsController
117:  * @return string|boolean
118:  */
119:     public function bake($type, $className) {
120:         $plugin = null;
121:         if ($this->plugin) {
122:             $plugin = $this->plugin . '.';
123:         }
124: 
125:         $realType = $this->mapType($type, $plugin);
126:         $fullClassName = $this->getRealClassName($type, $className);
127: 
128:         if ($this->typeCanDetectFixtures($type) && $this->isLoadableClass($realType, $fullClassName)) {
129:             $this->out(__d('cake_console', 'Bake is detecting possible fixtures...'));
130:             $testSubject = $this->buildTestSubject($type, $className);
131:             $this->generateFixtureList($testSubject);
132:         } elseif ($this->interactive) {
133:             $this->getUserFixtures();
134:         }
135:         App::uses($fullClassName, $realType);
136: 
137:         $methods = array();
138:         if (class_exists($fullClassName)) {
139:             $methods = $this->getTestableMethods($fullClassName);
140:         }
141:         $mock = $this->hasMockClass($type, $fullClassName);
142:         $construction = $this->generateConstructor($type, $fullClassName);
143: 
144:         $this->out("\n" . __d('cake_console', 'Baking test case for %s %s ...', $className, $type), 1, Shell::QUIET);
145: 
146:         $this->Template->set('fixtures', $this->_fixtures);
147:         $this->Template->set('plugin', $plugin);
148:         $this->Template->set(compact(
149:             'className', 'methods', 'type', 'fullClassName', 'mock',
150:             'construction', 'realType'
151:         ));
152:         $out = $this->Template->generate('classes', 'test');
153: 
154:         $filename = $this->testCaseFileName($type, $className);
155:         $made = $this->createFile($filename, $out);
156:         if ($made) {
157:             return $out;
158:         }
159:         return false;
160:     }
161: 
162: /**
163:  * Interact with the user and get their chosen type. Can exit the script.
164:  *
165:  * @return string Users chosen type.
166:  */
167:     public function getObjectType() {
168:         $this->hr();
169:         $this->out(__d('cake_console', 'Select an object type:'));
170:         $this->hr();
171: 
172:         $keys = array();
173:         $i = 0;
174:         foreach ($this->classTypes as $option => $package) {
175:             $this->out(++$i . '. ' . $option);
176:             $keys[] = $i;
177:         }
178:         $keys[] = 'q';
179:         $selection = $this->in(__d('cake_console', 'Enter the type of object to bake a test for or (q)uit'), $keys, 'q');
180:         if ($selection == 'q') {
181:             return $this->_stop();
182:         }
183:         $types = array_keys($this->classTypes);
184:         return $types[$selection - 1];
185:     }
186: 
187: /**
188:  * Get the user chosen Class name for the chosen type
189:  *
190:  * @param string $objectType Type of object to list classes for i.e. Model, Controller.
191:  * @return string Class name the user chose.
192:  */
193:     public function getClassName($objectType) {
194:         $type = ucfirst(strtolower($objectType));
195:         $typeLength = strlen($type);
196:         $type = $this->classTypes[$type];
197:         if ($this->plugin) {
198:             $plugin = $this->plugin . '.';
199:             $options = App::objects($plugin . $type);
200:         } else {
201:             $options = App::objects($type);
202:         }
203:         $this->out(__d('cake_console', 'Choose a %s class', $objectType));
204:         $keys = array();
205:         foreach ($options as $key => $option) {
206:             $this->out(++$key . '. ' . $option);
207:             $keys[] = $key;
208:         }
209:         while (empty($selection)) {
210:             $selection = $this->in(__d('cake_console', 'Choose an existing class, or enter the name of a class that does not exist'));
211:             if (is_numeric($selection) && isset($options[$selection - 1])) {
212:                 $selection = $options[$selection - 1];
213:             }
214:             if ($type !== 'Model') {
215:                 $selection = substr($selection, 0, $typeLength * - 1);
216:             }
217:         }
218:         return $selection;
219:     }
220: 
221: /**
222:  * Checks whether the chosen type can find its own fixtures.
223:  * Currently only model, and controller are supported
224:  *
225:  * @param string $type The Type of object you are generating tests for eg. controller
226:  * @return boolean
227:  */
228:     public function typeCanDetectFixtures($type) {
229:         $type = strtolower($type);
230:         return in_array($type, array('controller', 'model'));
231:     }
232: 
233: /**
234:  * Check if a class with the given package is loaded or can be loaded.
235:  *
236:  * @param string $package The package of object you are generating tests for eg. controller
237:  * @param string $class the Classname of the class the test is being generated for.
238:  * @return boolean
239:  */
240:     public function isLoadableClass($package, $class) {
241:         App::uses($class, $package);
242:         return class_exists($class);
243:     }
244: 
245: /**
246:  * Construct an instance of the class to be tested.
247:  * So that fixtures can be detected
248:  *
249:  * @param string $type The Type of object you are generating tests for eg. controller
250:  * @param string $class the Classname of the class the test is being generated for.
251:  * @return object And instance of the class that is going to be tested.
252:  */
253:     public function &buildTestSubject($type, $class) {
254:         ClassRegistry::flush();
255:         App::import($type, $class);
256:         $class = $this->getRealClassName($type, $class);
257:         if (strtolower($type) == 'model') {
258:             $instance = ClassRegistry::init($class);
259:         } else {
260:             $instance = new $class();
261:         }
262:         return $instance;
263:     }
264: 
265: /**
266:  * Gets the real class name from the cake short form. If the class name is already
267:  * suffixed with the type, the type will not be duplicated.
268:  *
269:  * @param string $type The Type of object you are generating tests for eg. controller
270:  * @param string $class the Classname of the class the test is being generated for.
271:  * @return string Real classname
272:  */
273:     public function getRealClassName($type, $class) {
274:         if (strtolower($type) == 'model' || empty($this->classTypes[$type])) {
275:             return $class;
276:         }
277:         if (strlen($class) - strpos($class, $type) == strlen($type)) {
278:             return $class;
279:         }
280:         return $class . $type;
281:     }
282: 
283: /**
284:  * Map the types that TestTask uses to concrete types that App::uses can use.
285:  *
286:  * @param string $type The type of thing having a test generated.
287:  * @param string $plugin The plugin name.
288:  * @return string
289:  */
290:     public function mapType($type, $plugin) {
291:         $type = ucfirst($type);
292:         if (empty($this->classTypes[$type])) {
293:             throw new CakeException(__d('cake_dev', 'Invalid object type.'));
294:         }
295:         $real = $this->classTypes[$type];
296:         if ($plugin) {
297:             $real = trim($plugin, '.') . '.' . $real;
298:         }
299:         return $real;
300:     }
301: 
302: /**
303:  * Get methods declared in the class given.
304:  * No parent methods will be returned
305:  *
306:  * @param string $className Name of class to look at.
307:  * @return array Array of method names.
308:  */
309:     public function getTestableMethods($className) {
310:         $classMethods = get_class_methods($className);
311:         $parentMethods = get_class_methods(get_parent_class($className));
312:         $thisMethods = array_diff($classMethods, $parentMethods);
313:         $out = array();
314:         foreach ($thisMethods as $method) {
315:             if (substr($method, 0, 1) != '_' && $method != strtolower($className)) {
316:                 $out[] = $method;
317:             }
318:         }
319:         return $out;
320:     }
321: 
322: /**
323:  * Generate the list of fixtures that will be required to run this test based on
324:  * loaded models.
325:  *
326:  * @param object $subject The object you want to generate fixtures for.
327:  * @return array Array of fixtures to be included in the test.
328:  */
329:     public function generateFixtureList($subject) {
330:         $this->_fixtures = array();
331:         if (is_a($subject, 'Model')) {
332:             $this->_processModel($subject);
333:         } elseif (is_a($subject, 'Controller')) {
334:             $this->_processController($subject);
335:         }
336:         return array_values($this->_fixtures);
337:     }
338: 
339: /**
340:  * Process a model recursively and pull out all the
341:  * model names converting them to fixture names.
342:  *
343:  * @param Model $subject A Model class to scan for associations and pull fixtures off of.
344:  * @return void
345:  */
346:     protected function _processModel($subject) {
347:         $this->_addFixture($subject->name);
348:         $associated = $subject->getAssociated();
349:         foreach ($associated as $alias => $type) {
350:             $className = $subject->{$alias}->name;
351:             if (!isset($this->_fixtures[$className])) {
352:                 $this->_processModel($subject->{$alias});
353:             }
354:             if ($type == 'hasAndBelongsToMany') {
355:                 $joinModel = Inflector::classify($subject->hasAndBelongsToMany[$alias]['joinTable']);
356:                 if (!isset($this->_fixtures[$joinModel])) {
357:                     $this->_processModel($subject->{$joinModel});
358:                 }
359:             }
360:         }
361:     }
362: 
363: /**
364:  * Process all the models attached to a controller
365:  * and generate a fixture list.
366:  *
367:  * @param Controller $subject A controller to pull model names off of.
368:  * @return void
369:  */
370:     protected function _processController($subject) {
371:         $subject->constructClasses();
372:         $models = array(Inflector::classify($subject->name));
373:         if (!empty($subject->uses)) {
374:             $models = $subject->uses;
375:         }
376:         foreach ($models as $model) {
377:             $this->_processModel($subject->{$model});
378:         }
379:     }
380: 
381: /**
382:  * Add classname to the fixture list.
383:  * Sets the app. or plugin.plugin_name. prefix.
384:  *
385:  * @param string $name Name of the Model class that a fixture might be required for.
386:  * @return void
387:  */
388:     protected function _addFixture($name) {
389:         $parent = get_parent_class($name);
390:         $prefix = 'app.';
391:         if (strtolower($parent) != 'appmodel' && strtolower(substr($parent, -8)) == 'appmodel') {
392:             $pluginName = substr($parent, 0, strlen($parent) -8);
393:             $prefix = 'plugin.' . Inflector::underscore($pluginName) . '.';
394:         }
395:         $fixture = $prefix . Inflector::underscore($name);
396:         $this->_fixtures[$name] = $fixture;
397:     }
398: 
399: /**
400:  * Interact with the user to get additional fixtures they want to use.
401:  *
402:  * @return array Array of fixtures the user wants to add.
403:  */
404:     public function getUserFixtures() {
405:         $proceed = $this->in(__d('cake_console', 'Bake could not detect fixtures, would you like to add some?'), array('y', 'n'), 'n');
406:         $fixtures = array();
407:         if (strtolower($proceed) == 'y') {
408:             $fixtureList = $this->in(__d('cake_console', "Please provide a comma separated list of the fixtures names you'd like to use.\nExample: 'app.comment, app.post, plugin.forums.post'"));
409:             $fixtureListTrimmed = str_replace(' ', '', $fixtureList);
410:             $fixtures = explode(',', $fixtureListTrimmed);
411:         }
412:         $this->_fixtures = array_merge($this->_fixtures, $fixtures);
413:         return $fixtures;
414:     }
415: 
416: /**
417:  * Is a mock class required for this type of test?
418:  * Controllers require a mock class.
419:  *
420:  * @param string $type The type of object tests are being generated for eg. controller.
421:  * @return boolean
422:  */
423:     public function hasMockClass($type) {
424:         $type = strtolower($type);
425:         return $type == 'controller';
426:     }
427: 
428: /**
429:  * Generate a constructor code snippet for the type and classname
430:  *
431:  * @param string $type The Type of object you are generating tests for eg. controller
432:  * @param string $fullClassName The Classname of the class the test is being generated for.
433:  * @return string Constructor snippet for the thing you are building.
434:  */
435:     public function generateConstructor($type, $fullClassName) {
436:         $type = strtolower($type);
437:         if ($type == 'model') {
438:             return "ClassRegistry::init('$fullClassName');\n";
439:         }
440:         if ($type == 'controller') {
441:             $className = substr($fullClassName, 0, strlen($fullClassName) - 10);
442:             return "new Test$fullClassName();\n\t\t\$this->{$className}->constructClasses();\n";
443:         }
444:         return "new $fullClassName();\n";
445:     }
446: 
447: /**
448:  * Make the filename for the test case. resolve the suffixes for controllers
449:  * and get the plugin path if needed.
450:  *
451:  * @param string $type The Type of object you are generating tests for eg. controller
452:  * @param string $className the Classname of the class the test is being generated for.
453:  * @return string filename the test should be created on.
454:  */
455:     public function testCaseFileName($type, $className) {
456:         $path = $this->getPath() . 'Case' . DS;
457:         $type = Inflector::camelize($type);
458:         if (isset($this->classTypes[$type])) {
459:             $path .= $this->classTypes[$type] . DS;
460:         }
461:         $className = $this->getRealClassName($type, $className);
462:         return str_replace('/', DS, $path) . Inflector::camelize($className) . 'Test.php';
463:     }
464: 
465: /**
466:  * get the option parser.
467:  *
468:  * @return void
469:  */
470:     public function getOptionParser() {
471:         $parser = parent::getOptionParser();
472:         return $parser->description(__d('cake_console', 'Bake test case skeletons for classes.'))
473:             ->addArgument('type', array(
474:                 'help' => __d('cake_console', 'Type of class to bake, can be any of the following: controller, model, helper, component or behavior.'),
475:                 'choices' => array(
476:                     'Controller', 'controller',
477:                     'Model', 'model',
478:                     'Helper', 'helper',
479:                     'Component', 'component',
480:                     'Behavior', 'behavior'
481:                 )
482:             ))->addArgument('name', array(
483:                 'help' => __d('cake_console', 'An existing class to bake tests for.')
484:             ))->addOption('plugin', array(
485:                 'short' => 'p',
486:                 'help' => __d('cake_console', 'CamelCased name of the plugin to bake tests for.')
487:             ))->epilog(__d('cake_console', 'Omitting all arguments and options will enter into an interactive mode.'));
488:     }
489: }
490: 
OpenHub
Rackspace
Rackspace
  • Business Solutions
  • Showcase
  • Documentation
  • Book
  • API
  • Videos
  • Reporting Security Issues
  • Privacy Policy
  • Logos & Trademarks
  • Community
  • Get Involved
  • Issues (GitHub)
  • Bakery
  • Featured Resources
  • Training
  • Meetups
  • My CakePHP
  • CakeFest
  • Newsletter
  • Linkedin
  • YouTube
  • Facebook
  • Twitter
  • Mastodon
  • Help & Support
  • Forum
  • Stack Overflow
  • Slack
  • Paid Support

Generated using CakePHP API Docs