views:

702

answers:

3

According to this mailing list discussion, the recommended way to access the application resources in a Zend MVC controller is:

$this->getInvokeArg('bootstrap')->getResource('foo');

This works in production (when browsing to the corresponding Web page). However, when testing a controller action containing this code with Zend_Test_PHPUnit_ControllerTestCase, I get:

PHP Fatal error: Call to a member function getResource() on a non-object in .../application/controllers/IndexController.php on line 12

Until introducing that getInvokeArg thing, the tests ran just fine. The question is, how can I make the “recommended” way of accessing resources work in the test harness?

Just checked: $this->getFrontController()->getParam('bootstrap')->getResource('foo') doesn't work either.

UPDATE: I do call the application bootstrap with phpunit --bootstrap ./scripts/application_bootstrap.php ... and I know it executes fine.

And there I have:

$application = new Zend_Application(
    APPLICATION_ENV,
    APPLICATION_PATH . '/configs/application.ini'
);

$application->bootstrap();
+3  A: 

Edit: I'm sorry, I missed that part of your question the first time around (no coffee yet!). I've edited the answer.

The problem lies in that Zend_Test_PHPUnit_ControllerTestCase is meant to unit test controllers. It tries to use very few dependencies from the rest of the framework, and it does NOT automatically run/bootstrap/whatever (and is not aware of) your larger application.

Basically, getResouce doesn't work, because your bootstrap does not exist.

If you take a look around the Zend_Test_PHPUnit_ControllerTestCase you'll see that it manually sets up and uses Zend_Controller_Front, and initializes its own request/response objects for each test. It's attempting to be as much of a unit test as possible, not a functional or integration test.

To solve your problem, you have to tell the test suite how to bootstrap your application. There's a couple ways to achieve this.

One, you can assign a file to the public bootstrap property of the test class.

public $bootstrap = '/path/to/bootstrap/file.php'

This is a good option for applications using Zend_Application.

Or, by providing a callback to the bootstrap property:

public function setUp()
{
    // Use the 'start' method of a Bootstrap object instance:
    $bootstrap = new Bootstrap('test');
    $this->bootstrap = array($bootstrap, 'start');
    parent::setUp();
}

Note: It's really important to call the parent setUp method if you override it.

Edit 2: Ok, you are actually bootstrapping your application. So, you've done the above, and still no luck.

I would recommend overriding the dispatch method of the TestCase, to ensure that it is receiving the bootstrap. (This'll work for sure).

public function dispatch($url = null)
{
    $this->getFrontController()->setParam('bootstrap', $yourBootstrap);
    parent::dispatch($url);
}

Come to think of it, you could probably do that in the setUp as well.

This will allow the test suite to properly mimic the behaviour of a front controller constructed by Zend_Application. Both methods of accessing the bootstrap should work now.

jason
The breaking code is in Zend_Controller_Action's subclass — a controller, not in a test case
Ivan Krechetov
Jason, I do call the bootstrap routine: phpunit --bootstrap ./scripts/application_bootstrap.php, and I'm sure it runs (by $application->bootstrap();), as all the resources are available via Zend_Registry, where I put stuff on init.
Ivan Krechetov
Thanks for this solution. I spent hours thinking it was a problem with my test bootstrap itself. Zend_Test really needs better documentation, especially as it relates to Zend_Application.
Bryan M.
+6  A: 

I ran into the same issue when using a controller plugin that needed the bootstrap.

Basically I created an abstract class and inherited from it.


abstract class My_ControllerTestCase extends Zend_Test_PHPUnit_ControllerTestCase
{
    protected $application;

    public function setUp()
    {


        $this->bootstrap = array($this, 'appBootstrap');


        return parent::setUp();
    }

    public function appBootstrap()
       {

        $this->application = new Zend_Application(APPLICATION_ENV, APPLICATION_PATH . '/config/app.ini');

        $this->application->bootstrap();

        $bootstrap = $this->application->getBootstrap();

        $front = $bootstrap->getResource('FrontController');

        $front->setParam('bootstrap', $bootstrap);

       }
}

Then you use as follows:


class MyControllerTest extends My_ControllerTestCase
{

}

I also logged a request to have this functionality become apart of ZF

[ZF-7373]: (http://framework.zend.com/issues/browse/ZF-7373) -

Leaving a comment would help to highlight this for inclusion.

ksharpe
A: 

Oh for crying out loud, thank you, thank you, thank you ksharpe. This has been driving me absolutely crazy!!! Your solution works perfectly.

Jason
Mark is as the answer then!
jakenoble