Latest: Genstatic, my first sip of coffee

Content with Style

Web Technique

Unit testing controllers with Zend Framework

by Pascal Opitz on April 2 2009, 14:47

Unit testing your Zend Framework driven MVC applications is not hard at all. This post tries to give a brief overview on how to test your Controllers with Zend_Test.

Install PHPUnit

Zend Framework integrates with PHPUnit. PHPUnit is a PEAR module, and the usual way is to install PEAR that first and then use the pear installer to install PHPUnit for you.

Create an application bootstrap include

If you're using the front controller pattern and have all your variables set up in the index.php of your web folder, now is the time to move things into an include file, before you create the controller and dispatcher. We'll use this very file when doing unit tests with Zend_Test

Start Testing Controllers

With the help of Zend_Test we're now able to create tests that dispatch our controllers. We're then able to do assertions against the response, using the provided methods of the ControllerTestCase.


class IndexTest extends Zend_Test_PHPUnit_ControllerTestCase
{
  public function setUp() {
    $this->bootstrap = array($this, 'appBootstrap');
    parent::setUp();
  }

  public tearDown() {
    $this->resetRequest();
    $this->resetResponse();
    parent::tearDown();
  }

  public function appBootstrap() {
    require_once('/my/bootstrap.php');
  }

  public function testHomepage() {
    $this->dispatch('/');
    $this->assertController('index');
    $this->assertAction('index');
    $this->assertXpath("//form[@action = '/foo']");
  }
}

Manipulate the request, evaluate the response

Now that we've dispatched our controller, we can manipulate the request and access the response, to see what the dispatched action does. For example an action that requires post data, and then redirects us to another page:


public testPostAction() {
  $request = $this->getRequest();

  $request->setMethod('POST');
  $request->setPost(array(
    'foo' => 'bar',
    'baz' => 'x',
  ));

  $this->dispatch('/my/post/action');
  $this->assertRedirectTo('/my/expected/redirect');
}

Checking data manipulation

If we're working with data manipulations in our tests, we could use models to create the data we manipulate, and then to evaluate it after the controller has been dispatched. Finally we'll clean up after the test. Of course you'd want to have stable models at that point.


public function testSave() {
  $my_id = $model->create();

  $request = $this->getRequest();
  
  $request->setMethod('POST');
  $request->setPost(array(
    'name' => 'unittest modified',
    'id' => $my_id,
  ));

  $this->dispatch("/save/object/");
  $this->assertRedirectRegex('#/edit/object/id/[\d]+$#');

  $data = $model->get($my_id);
  $this->assertEquals($data['name'], 'unittest modified');
  
  $my_id = $model->delete($my_id);  
}

Related links

Comments

  • Hi, it works fine, till iam trying to setup a second testCase, than i recieve the error " no default module .. " any suggestions ?

    by Arne on April 19 2009, 13:07 - #

  • Hard to say. Are you using module based architecture? Maybe set the controller directory in the appBootstrap function:

    
    public function appBootstrap() {
      require_once('/my/bootstrap.php');
      $this->frontController->setControllerDirectory('/my/path/', 'default'); 
      $this->frontController->addModuleDirectory('/path/modules'); 
    }
    

    by Pascal Opitz on April 19 2009, 13:29 - #

  • I have the same issue as Arne:

    the first test will pass or fail as expected, but if I add a second test I get the error "no default module defined for this application".

    On another note... I had a separate issue using Zend_Test to test controllers where any of the Zend assertions would throw the error "undefined method: incrementAssertionCounter()" or something to that effect. In case someone else that stumbles across here is having the same issue... I was able to track it down to an issue with PHPUnit 3.3.3 and after rolling back to 3.3.16 everything works fine. There is a ticket for this issue on the ZF Issue Tracker at http://framework.zend.com/issues/browse/ZF-4839

    by Brian Hazzard on April 24 2009, 14:38 - #

  • I encountered issues for testing a controller that displays form with captchas - so I wrote an article to help.

    Regards...

    by Giorgio Sironi on April 25 2009, 09:46 - #

  • Hi Folks,

    i've found the error, or better the mistake.

    There were some missing Action_Plugins / View_Helper and other stuff. The Zend_Controller_Testcase is not throwing exceptions or such stuff.

    So the class cant find the plugin, is looking for the errorcontroller and next test is failing..

    You can see the errors if you use something like this in your controller testecase :

    $x = $this->getFrontController()->getResponse();
    error_log(var_export($x));
    

    by Arne on April 27 2009, 08:03 - #

  • What I did to fix my issues with the second test's errors:

    
    public function setUp()
    {
        $this->bootstrap = array($this, 'appBootstrap');
        parent::setUp();
    }
    
    public function appBootstrap()
    {
        require dirname(__FILE__) . '/../test-bootstrap.php';
    }
    
    public function tearDown()
    {
        $this->resetRequest();
        $this->resetResponse();
        parent::tearDown();
    }
    

    The reason the problem was occuring was that test-bootstrap.php defined the default module in FrontController setControllerDir or something like that. I used to have require_once, but then for subsequent requests the bootstrap wasn't being reevaluated... which led to no default module being defined. In my tearDown I resetRequest and resetResponse for good measure.

    by Brian Hazzard on April 28 2009, 17:38 - #

  • thanks brian I had the same issue your solution worked fine!!!!

    by Daniel Alvarez on May 27 2009, 22:54 - #

  • Hi, The link move things into an include file is dead (which is a shame, since I can't get my controller's unit tests to work since the beginning of my project, 2 months ago...). I looked for "create a bootstrap file" in the site's search engine (in the to-right corner) but didn't find anything at the 5th page, so I gave up. I'll try to make your exemlpe work on my architecture, though :)

    by andy_b_84 on July 15 2009, 13:50 - #

  • Bootstrapping changed in ZF 1.8, and they also took down the deprecated quickstart guide. Here is the new one, but it's slightly different.

    by Matthias Willerich on July 16 2009, 14:17 - #

  • very useful article especially for who just started learning zend :-) thx!

    by myltik on June 4 2010, 09:08 - #

  • I've set up my testsuite just fine and everything seems to be working properly. Any one test I use evaluates perfectly fine.

    But once I add a second test to any test-class (e.g. IndexControllerTest) I'm getting an "Undefined Index: demo" error regardless of the second tests content. (even if it's an empty method/function)

    The same is true for any subsequent test I add to the test class.

    Anyone any idea as to what might cause this behaviour or what I might do in order to fix it?

    by Uwe on August 30 2010, 04:39 - #


Comments for this article are closed.