CakeFest #3: July 9-12 2009 Berlin!

4.7.6 Testing controllers

4.7.6.1 Creating a test case

Say you have a typical articles controller, with its corresponding model, and it looks like this:

<?php 
class ArticlesController extends AppController { 
   var $name = 'Articles'; 
   var $helpers = array('Ajax', 'Form', 'Html'); 
   
   function index($short = null) { 
     if (!empty($this->data)) { 
       $this->Article->save($this->data); 
     } 
     if (!empty($short)) { 
       $result = $this->Article->findAll(null, array('id', 
          'title')); 
     } else { 
       $result = $this->Article->findAll(); 
     } 
 
     if (isset($this->params['requested'])) { 
       return $result; 
     } 
 
     $this->set('title', 'Articles'); 
     $this->set('articles', $result); 
   } 
} 
?>
  1. <?php
  2. class ArticlesController extends AppController {
  3. var $name = 'Articles';
  4. var $helpers = array('Ajax', 'Form', 'Html');
  5. function index($short = null) {
  6. if (!empty($this->data)) {
  7. $this->Article->save($this->data);
  8. }
  9. if (!empty($short)) {
  10. $result = $this->Article->findAll(null, array('id',
  11. 'title'));
  12. } else {
  13. $result = $this->Article->findAll();
  14. }
  15. if (isset($this->params['requested'])) {
  16. return $result;
  17. }
  18. $this->set('title', 'Articles');
  19. $this->set('articles', $result);
  20. }
  21. }
  22. ?>

Create a file named articles_controller.test.php in your app/tests/cases/controllers directory and put the following inside:

<?php 
class ArticlesControllerTest extends CakeTestCase { 
   function startCase() { 
     echo '<h1>Starting Test Case</h1>'; 
   } 
   function endCase() { 
     echo '<h1>Ending Test Case</h1>'; 
   } 
   function startTest($method) { 
     echo '<h3>Starting method ' . $method . '</h3>'; 
   } 
   function endTest($method) { 
     echo '<hr />'; 
   } 
   function testIndex() { 
     $result = $this->testAction('/articles/index'); 
     debug($result); 
   } 
   function testIndexShort() { 
     $result = $this->testAction('/articles/index/short'); 
     debug($result); 
   } 
   function testIndexShortGetRenderedHtml() { 
     $result = $this->testAction('/articles/index/short', 
     array('return' => 'render')); 
     debug(htmlentities($result)); 
   } 
   function testIndexShortGetViewVars() { 
     $result = $this->testAction('/articles/index/short', 
     array('return' => 'vars')); 
     debug($result); 
   } 
   function testIndexFixturized() { 
     $result = $this->testAction('/articles/index/short', 
     array('fixturize' => true)); 
     debug($result); 
   } 
   function testIndexPostFixturized() { 
     $data = array('Article' => array('user_id' => 1, 'published' 
          => 1, 'slug'=>'new-article', 'title' => 'New Article', 'body' => 'New Body')); 
     $result = $this->testAction('/articles/index', 
     array('fixturize' => true, 'data' => $data, 'method' => 'post')); 
     debug($result); 
   } 
} 
?> 
  1. <?php
  2. class ArticlesControllerTest extends CakeTestCase {
  3. function startCase() {
  4. echo '<h1>Starting Test Case</h1>';
  5. }
  6. function endCase() {
  7. echo '<h1>Ending Test Case</h1>';
  8. }
  9. function startTest($method) {
  10. echo '<h3>Starting method ' . $method . '</h3>';
  11. }
  12. function endTest($method) {
  13. echo '<hr />';
  14. }
  15. function testIndex() {
  16. $result = $this->testAction('/articles/index');
  17. debug($result);
  18. }
  19. function testIndexShort() {
  20. $result = $this->testAction('/articles/index/short');
  21. debug($result);
  22. }
  23. function testIndexShortGetRenderedHtml() {
  24. $result = $this->testAction('/articles/index/short',
  25. array('return' => 'render'));
  26. debug(htmlentities($result));
  27. }
  28. function testIndexShortGetViewVars() {
  29. $result = $this->testAction('/articles/index/short',
  30. array('return' => 'vars'));
  31. debug($result);
  32. }
  33. function testIndexFixturized() {
  34. $result = $this->testAction('/articles/index/short',
  35. array('fixturize' => true));
  36. debug($result);
  37. }
  38. function testIndexPostFixturized() {
  39. $data = array('Article' => array('user_id' => 1, 'published'
  40. => 1, 'slug'=>'new-article', 'title' => 'New Article', 'body' => 'New Body'));
  41. $result = $this->testAction('/articles/index',
  42. array('fixturize' => true, 'data' => $data, 'method' => 'post'));
  43. debug($result);
  44. }
  45. }
  46. ?>

4.7.6.2 The testAction method

The new thing here is the testAction method. The first argument of that method is the Cake url of the controller action to be tested, as in '/articles/index/short'.

The second argument is an array of parameters, consisting of:

return
Set to what you want returned.
Valid values are:
  • 'vars' - You get the view vars available after executing action
  • 'view' - You get The rendered view, without the layout
  • 'contents' - You get the rendered view's complete html, including the layout
  • 'result' - You get the returned value when action uses $this->params['requested'].
The default is 'result'.
fixturize
Set to true if you want your models auto-fixturized (so your application tables get copied, along with their records, to test tables so if you change data it does not affect your real application.) If you set 'fixturize' to an array of models, then only those models will be auto-fixturized while the other will remain with live tables. If you wish to use your fixture files with testAction() do not use fixturize, and instead just use fixtures as you normally would.
method
set to 'post' or 'get' if you want to pass data to the controller
data
the data to be passed. Set it to be an associative array consisting of fields => value. Take a look at function testIndexPostFixturized() in above test case to see how we emulate posting form data for a new article submission.

4.7.6.3 Pitfalls

If you use testAction to test a method in a controller that does a redirect, your test will terminate immediately, not yielding any results.
See https://trac.cakephp.org/ticket/4154 for a possible fix.