views:

124

answers:

3

I've read a lot of Zend controller testing tutorials but I can't find one that explains how to test a controller that uses models and mocking those models.

I have the following controller action:-

function indexAction(){
  // Get the cache used by the application
  $cache = $this->getCache(); 

  // Get the index service client and model
  $indexServiceClient = new IndexServiceClient($this->getConfig());
  $indexModel = $this->_helper->ModelLoader->load('admin_indexmodel', $cache);
  $indexModel->setIndexServiceClient($indexServiceClient);

  // Load all the indexes
  $indexes = $indexModel->loadIndexes();

  $this->view->assign('indexes', $indexes);
}

At the moment I have a very basic test case:-

public function testIndexActionRoute() {
  $this->dispatch( '/admin/index' );
  $this->assertModule('admin', 'Incorrect module used');
  $this->assertController('index', 'Incorrect controller used');
  $this->assertAction('index', 'Incorrect action used');
}

This test works but it's calling the real models and services, which sometimes means it times out and fails on the test environment. In order to properly unit test just the controller I need to have mocks and expectations for IndexServiceClient and IndexModel - how is this done?

A: 

http://blog.fedecarg.com/2009/11/01/testing-zend-framework-action-controllers-with-mocks/

jens
I had looked at that tutorial but could not find an example of injecting controller dependencies as mocks.
Mathew Attlee
+1  A: 

Well, since not many replies I am seeing here, I'll try to add my 2cents(potentially arguable). Answer written below is my IHMO and very subjective(and I think not very useful, but here we go anyway)

I think controllers are not a good fit unit testing. Your business logic layer, models etc. is what is unitestable. Controllers are connected with UI and bring system together so to speak - hence to me they are a better fit for integration and UI testing - something that packages such as Selenium are used for.

To my mind testing should be easy enough to implement such that total effort for testing implementation is adequate to the returns of it. Wiring up all dependencies for a controller seems to me(with my limited knowledge of course) a bit too much of a task.

The other way to think about it is - what is actually going on in your controllers. Again IHMO it supposed to be primarily a glue level between your business logic and your UI. If you're putting a lot of business logic into controller it will have an adverse effect(for instance it won't be easily unitestable..).

This is all sort of theory of course. Hopefully someone can provide a better answer and actually show how to easily wire up a controller for unit tests!

Alex N.
After discussing this with my colleagues the same conclusion was reached. Unit testing in controllers with proper mocks takes a lot of effort with little return. This is particularly true as our model layer does the majority of the work, and has extensive test coverage, whilst the controller is quite thin.
Mathew Attlee
A: 

One possible solution that a colleague put forward is to use a Zend Controller Action Helper to inject mock dependencies. This should work in theory but I've yet to extensively test this method

Here is an example of doing it this way.

class Mock_IndexModel_Helper extends Zend_Controller_Action_Helper_Abstract {

    private $model;

    public function __construct($model) {
        $this->model = $model;
    }   

    /**
     * Init hook
     */
    public function init() {            
        $this->getActionController()->setIndexModel( $this->model );
    }

}


class IndexControllerTest extends Zend_Test_PHPUnit_ControllerTestCase {

    public $bootstrap = BOOTSTRAP;

    public function setUp(){
        parent::setUp();
    }


    /**
    * Test the correct module/controller/action are used
    */
    public function testIndexAction() {              
        $mockIndexModel = $this->getMock("IndexModel");

        Zend_Controller_Action_HelperBroker::addHelper(new Mock_IndexModel_Helper($mockIndexModel));

        $this->dispatch( '/admin/index' );

        $this->assertModule('admin', 'Incorrect module used');
        $this->assertController('index', 'Incorrect controller used');
        $this->assertAction('index', 'Incorrect action used');
    }    
}
Mathew Attlee