Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Test generation for models is partially complete.

fixtures and introspected methods work.
  • Loading branch information...
commit 587b0ca07d3e6fe0e71a25eff4abdd750d2e7831 1 parent eb9f40c
@markstory markstory authored
View
259 cake/console/libs/tasks/test.php
@@ -47,6 +47,13 @@ class TestTask extends Shell {
var $path = TESTS;
/**
+ * Tasks used.
+ *
+ * @var array
+ **/
+ var $tasks = array('Template');
+
+/**
* class types that methods can be generated for
*
* @var array
@@ -54,6 +61,20 @@ class TestTask extends Shell {
var $classTypes = array('Model', 'Controller', 'Component', 'Behavior', 'Helper');
/**
+ * Internal list of fixtures that have been added so far.
+ *
+ * @var string
+ **/
+ var $_fixtures = array();
+
+/**
+ * Flag for interactive mode
+ *
+ * @var boolean
+ **/
+ var $interactive = false;
+
+/**
* Execution method always used for tasks
*
* @access public
@@ -68,8 +89,8 @@ function execute() {
}
if (count($this->args) > 1) {
- $class = Inflector::underscore($this->args[0]);
- if ($this->bake($class, $this->args[1])) {
+ $type = Inflector::underscore($this->args[0]);
+ if ($this->bake($type, $this->args[1])) {
$this->out('done');
}
}
@@ -81,6 +102,7 @@ function execute() {
* @access private
*/
function __interactive($type = null) {
+ $this->interactive = true;
$this->hr();
$this->out(__('Bake Tests', true));
$this->out(sprintf(__("Path: %s", true), $this->path));
@@ -89,75 +111,54 @@ function __interactive($type = null) {
$selection = null;
if ($type) {
$type = Inflector::camelize($type);
- if (in_array($type, $this->classTypes)) {
- $selection = array_search($type);
+ if (!in_array($type, $this->classTypes)) {
+ unset($type);
}
}
- if (!$selection) {
- $selection = $this->getObjectType();
- }
- /*
-
- $key = $this->in(__("Enter the class to bake a test for or (q)uit", true), $keys, 'q');
-
- if ($key != 'q') {
- if (isset($options[--$key])) {
- $class = $options[$key];
- }
-
- if ($class) {
- $name = $this->in(__("Enter the name for the test or (q)uit", true), null, 'q');
- if ($name !== 'q') {
- $case = null;
- while ($case !== 'q') {
- $case = $this->in(__("Enter a test case or (q)uit", true), null, 'q');
- if ($case !== 'q') {
- $cases[] = $case;
- }
- }
- if ($this->bake($class, $name, $cases)) {
- $this->out(__("Test baked\n", true));
- $type = null;
- }
- $class = null;
- }
- }
- } else {
- $this->_stop();
- }
+ if (!$type) {
+ $type = $this->getObjectType();
}
- */
+ $className = $this->getClassName($type);
+ return $this->bake($type, $className);
}
/**
- * Interact with the user and get their chosen type. Can exit the script.
+ * Completes final steps for generating data to create test case.
*
- * @return int Index of users chosen object type.
- **/
- function getObjectType() {
- $this->hr();
- $this->out(__("Select an object type:", true));
- $this->hr();
+ * @param string $type Type of object to bake test case for ie. Model, Controller
+ * @param string $className the 'cake name' for the class ie. Posts for the PostsController
+ * @access public
+ */
+ function bake($type, $className) {
+ if ($this->typeCanDetectFixtures($type) && $this->isLoadableClass($type, $className)) {
+ $this->out(__('Bake is detecting possible fixtures..', true));
+ $testSubject =& $this->buildTestSubject($type, $className);
+ $this->generateFixtureList($testSubject);
+ } elseif ($this->interactive) {
+ $this->getUserFixtures();
+ }
+ $fullClassName = $this->getRealClassName($type, $className);
- $keys = array();
- foreach ($this->classTypes as $key => $option) {
- $this->out(++$key . '. ' . $option);
- $keys[] = $key;
+ $methods = array();
+ if (class_exists($fullClassName)) {
+ $methods = $this->getTestableMethods($fullClassName);
}
- $keys[] = 'q';
- $selection = $this->in(__("Enter the type of object to bake a test for or (q)uit", true), $keys, 'q');
- if ($selection == 'q') {
- $this->_stop();
+
+ $this->Template->set('fixtures', $this->_fixtures);
+ $this->Template->set('plugin', $this->plugin);
+ $this->Template->set(compact('className', 'methods', 'type', 'fullClassName'));
+ $out = $this->Template->generate('objects', 'test');
+
+ if (strpos($this->path, $type) === false) {
+ $this->filePath = $this->path . 'cases' . DS . Inflector::tableize($type) . DS;
}
- return $selection;
- }
+ $made = $this->createFile($this->filePath . Inflector::underscore($className) . '.test.php', $out);
+ if ($made) {
+ return $out;
+ }
+ return false;
-/**
- * Writes File
- *
- * @access public
- */
- function bake($class, $name = null, $cases = array()) {
+ /*
if (!$name) {
return false;
}
@@ -205,23 +206,102 @@ function bake($class, $name = null, $cases = array()) {
}
$header = '$Id';
- $content = "<?php \n/* SVN FILE: $header$ */\n/* ". $name ." Test cases generated on: " . date('Y-m-d H:m:s') . " : ". time() . "*/\n{$out}?>";
+ $content = "<?php \n/* SVN FILE: $header$ * /\n/* ". $name ." Test cases generated on: " . date('Y-m-d H:m:s') . " : ". time() . "* /\n{$out}?>";
return $this->createFile($this->filePath . Inflector::underscore($name) . '.test.php', $content);
+ */
}
+
/**
- * Handles the extra stuff needed
+ * Interact with the user and get their chosen type. Can exit the script.
*
- * @access private
- */
- function __extras($class) {
- $extras = null;
- switch ($class) {
- case 'Model':
- $extras = "\n\tvar \$cacheSources = false;";
- $extras .= "\n\tvar \$useDbConfig = 'test_suite';\n";
- break;
+ * @return string Users chosen type.
+ **/
+ function getObjectType() {
+ $this->hr();
+ $this->out(__("Select an object type:", true));
+ $this->hr();
+
+ $keys = array();
+ foreach ($this->classTypes as $key => $option) {
+ $this->out(++$key . '. ' . $option);
+ $keys[] = $key;
}
- return $extras;
+ $keys[] = 'q';
+ $selection = $this->in(__("Enter the type of object to bake a test for or (q)uit", true), $keys, 'q');
+ if ($selection == 'q') {
+ return $this->_stop();
+ }
+ return $this->classTypes[$selection - 1];
+ }
+
+/**
+ * Get the user chosen Class name for the chosen type
+ *
+ * @param string $objectType Type of object to list classes for i.e. Model, Controller.
+ * @return string Class name the user chose.
+ **/
+ function getClassName($objectType) {
+ $options = Configure::listObjects(strtolower($objectType));
+ $this->out(sprintf(__('Choose a %s class', true), $objectType));
+ $keys = array();
+ foreach ($options as $key => $option) {
+ $this->out(++$key . '. ' . $option);
+ $keys[] = $key;
+ }
+ $selection = $this->in(__('Choose an existing class, or enter the name of a class that does not exist', true));
+ if (isset($options[$selection - 1])) {
+ return $options[$selection - 1];
+ }
+ return $selection;
+ }
+
+/**
+ * Checks whether the chosen type can find its own fixtures.
+ * Currently only model, and controller are supported
+ *
+ * @return boolean
+ **/
+ function typeCanDetectFixtures($type) {
+ $type = strtolower($type);
+ return ($type == 'controller' || $type == 'model');
+ }
+
+/**
+ * Check if a class with the given type is loaded or can be loaded.
+ *
+ * @return boolean
+ **/
+ function isLoadableClass($type, $class) {
+ return App::import($type, $class);
+ }
+
+/**
+ * Construct an instance of the class to be tested.
+ * So that fixtures and methods can be detected
+ *
+ * @return object
+ **/
+ function &buildTestSubject($type, $class) {
+ App::import($type, $class);
+ $class = $this->getRealClassName($type, $class);
+ if (strtolower($type) == 'model') {
+ $instance =& ClassRegistry::init($class);
+ } else {
+ $instance =& new $class();
+ }
+ return $instance;
+ }
+
+/**
+ * Gets the real class name from the cake short form.
+ *
+ * @return string Real classname
+ **/
+ function getRealClassName($type, $class) {
+ if (strtolower($type) == 'model') {
+ return $class;
+ }
+ return $class . $type;
}
/**
@@ -266,6 +346,7 @@ function generateFixtureList(&$subject) {
* model names converting them to fixture names.
*
* @return void
+ * @access protected
**/
function _processModel(&$subject) {
$this->_addFixture($subject->name);
@@ -289,6 +370,7 @@ function _processModel(&$subject) {
* and generate a fixture list.
*
* @return void
+ * @access protected
**/
function _processController(&$subject) {
$subject->constructClasses();
@@ -306,6 +388,7 @@ function _processController(&$subject) {
* Sets the app. or plugin.plugin_name. prefix.
*
* @return void
+ * @access protected
**/
function _addFixture($name) {
$parent = get_parent_class($name);
@@ -319,6 +402,38 @@ function _addFixture($name) {
}
/**
+ * Interact with the user to get additional fixtures they want to use.
+ *
+ * @return void
+ **/
+ function getUserFixtures() {
+ $proceed = $this->in(__('Bake could not detect fixtures, would you like to add some?', true), array('y','n'), 'n');
+ $fixtures = array();
+ if (strtolower($proceed) == 'y') {
+ $fixtureList = $this->in(__("Please provide a comma separated list of the fixtures names you'd like to use.\nExample: 'app.comment, app.post, plugin.forums.post'", true));
+ $fixtureListTrimmed = str_replace(' ', '', $fixtureList);
+ $fixtures = explode(',', $fixtureListTrimmed);
+ }
+ $this->_fixtures = array_merge($this->_fixtures, $fixtures);
+ return $fixtures;
+ }
+
+/**
+ * Handles the extra stuff needed
+ *
+ * @access private
+ */
+ function __extras($class) {
+ $extras = null;
+ switch ($class) {
+ case 'Model':
+ $extras = "\n\tvar \$cacheSources = false;";
+ $extras .= "\n\tvar \$useDbConfig = 'test_suite';\n";
+ break;
+ }
+ return $extras;
+ }
+/**
* Create a test for a Model object.
*
* @return void
View
15 cake/console/libs/templates/objects/test.ctp
@@ -20,7 +20,18 @@
*/
echo "<?php\n";
?>
-class <?php echo $name; ?>TestCase extends CakeTestCase {
-
+App::import('<?php echo $type; ?>', '<?php echo $className;?>');
+
+class <?php echo $className; ?>TestCase extends CakeTestCase {
+<?php if (!empty($fixtures)): ?>
+ var $fixtures = array('<?php echo join("', '", $fixtures); ?>');
+
+<?php endif; ?>
+<?php foreach ($methods as $method): ?>
+ function test<?php echo Inflector::classify($method); ?>() {
+
+ }
+
+<?php endforeach;?>
}
<?php echo '?>'; ?>
View
109 cake/tests/cases/console/libs/tasks/test.test.php
@@ -40,6 +40,7 @@
if (!class_exists('TestTask')) {
require CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'test.php';
+ require CAKE . 'console' . DS . 'libs' . DS . 'tasks' . DS . 'template.php';
}
Mock::generatePartial(
@@ -48,20 +49,10 @@
);
Mock::generatePartial(
'TestTask', 'MockTestTask',
- array('in', '_stop', 'err', 'out', 'createFile')
+ array('in', '_stop', 'err', 'out', 'createFile', 'isLoadableClass')
);
/**
- * Test subject for method extraction
- *
- **/
-class TestTaskSubjectClass extends Object {
- function methodOne() { }
- function methodTwo() { }
- function _noTest() { }
-}
-
-/**
* Test subject models for fixture generation
**/
class TestTaskArticle extends Model {
@@ -81,6 +72,15 @@ class TestTaskArticle extends Model {
'associationForeignKey' => 'tag_id'
)
);
+ function doSomething() {
+
+ }
+ function doSomethingElse() {
+
+ }
+ function _innerMethod() {
+
+ }
}
class TestTaskTag extends Model {
var $name = 'TestTaskTag';
@@ -131,10 +131,12 @@ class TestTaskTest extends CakeTestCase {
* @return void
* @access public
*/
- function setUp() {
+ function startTest() {
$this->Dispatcher =& new TestTestTaskMockShellDispatcher();
+ $this->Dispatcher->shellPaths = Configure::read('shellPaths');
$this->Task =& new MockTestTask($this->Dispatcher);
- $this->Task->Dispatch = new $this->Dispatcher;
+ $this->Task->Dispatch =& $this->Dispatcher;
+ $this->Task->Template =& new TemplateTask($this->Dispatcher);
}
/**
@@ -143,7 +145,7 @@ function setUp() {
* @return void
* @access public
*/
- function tearDown() {
+ function endTest() {
ClassRegistry::flush();
}
@@ -175,8 +177,8 @@ function testFilePathGeneration () {
* @return void
**/
function testMethodIntrospection() {
- $result = $this->Task->getTestableMethods('TestTaskSubjectClass');
- $expected = array('methodOne', 'methodTwo');
+ $result = $this->Task->getTestableMethods('TestTaskArticle');
+ $expected = array('doSomething', 'doSomethingElse');
$this->assertEqual($result, $expected);
}
@@ -220,7 +222,80 @@ function testGetObjectType() {
$this->Task->setReturnValueAt(1, 'in', 2);
$result = $this->Task->getObjectType();
- $this->assertEqual($result, 2);
+ $this->assertEqual($result, $this->Task->classTypes[1]);
+ }
+
+/**
+ * test that getClassName returns the user choice as a classname.
+ *
+ * @return void
+ **/
+ function testGetClassName() {
+ $this->Task->setReturnValueAt(0, 'in', 'MyCustomClass');
+ $result = $this->Task->getClassName('Model');
+ $this->assertEqual($result, 'MyCustomClass');
+
+ $this->Task->setReturnValueAt(1, 'in', 1);
+ $result = $this->Task->getClassName('Model');
+ $options = Configure::listObjects('model');
+ $this->assertEqual($result, $options[0]);
+ }
+
+/**
+ * Test the user interaction for defining additional fixtures.
+ *
+ * @return void
+ **/
+ function testGetUserFixtures() {
+ $this->Task->setReturnValueAt(0, 'in', 'y');
+ $this->Task->setReturnValueAt(1, 'in', 'app.pizza, app.topping, app.side_dish');
+ $result = $this->Task->getUserFixtures();
+ $expected = array('app.pizza', 'app.topping', 'app.side_dish');
+ $this->assertEqual($result, $expected);
+ }
+
+/**
+ * test that resolving classnames works
+ *
+ * @return void
+ **/
+ function testGetRealClassname() {
+ $result = $this->Task->getRealClassname('Model', 'Post');
+ $this->assertEqual($result, 'Post');
+
+ $result = $this->Task->getRealClassname('Controller', 'Posts');
+ $this->assertEqual($result, 'PostsController');
+
+ $result = $this->Task->getRealClassname('Helper', 'Form');
+ $this->assertEqual($result, 'FormHelper');
+
+ $result = $this->Task->getRealClassname('Behavior', 'Containable');
+ $this->assertEqual($result, 'ContainableBehavior');
+
+ $result = $this->Task->getRealClassname('Component', 'Auth');
+ $this->assertEqual($result, 'AuthComponent');
+ }
+
+/**
+ * test baking files.
+ *
+ * @return void
+ **/
+ function testBake() {
+ $this->Task->setReturnValue('createFile', true);
+ $this->Task->setReturnValue('isLoadableClass', true);
+
+ $result = $this->Task->bake('Model', 'TestTaskArticle');
+
+ $this->assertPattern('/App::import\(\'Model\', \'TestTaskArticle\'\)/', $result);
+ $this->assertPattern('/class TestTaskArticleTestCase extends CakeTestCase/', $result);
+ $this->assertPattern('/function testDoSomething\(\)/', $result);
+ $this->assertPattern('/function testDoSomethingElse\(\)/', $result);
+
+ $this->assertPattern("/'app\.test_task_article'/", $result);
+ $this->assertPattern("/'plugin\.test_task\.test_task_comment'/", $result);
+ $this->assertPattern("/'app\.test_task_tag'/", $result);
+ $this->assertPattern("/'app\.articles_tag'/", $result);
}
}
?>
Please sign in to comment.
Something went wrong with that request. Please try again.