Question

I am suspecting that there's a problem with Controller Plugins redirecting when used in Zend Test?

I have a controller plugin like http://pastie.org/1422639 I have put echo statements for debugging. I have code for redirecting to login if user is not logged in

if (!$auth->hasIdentity()) {
  echo 'no id, ';
  // redirect to login page
  $req->setDispatched(true);
  $redirector = Zend_Controller_Action_HelperBroker::getStaticHelper('redirector');echo 'got redir, ';
  $redirector->gotoUrl('/auth/login?returnUrl=' . urlencode($req->getRequestUri()));echo 'redirecting, ';
} ...

I find that when unit tesing, eg

$this->dispatch('/projects');

The output I got was

projects (ok I requested the projects page/controller), no id (ok, I am not logged in), got redir (I got the redirector ok), redirecting (it seem to be redirecting ok ...), error (but I got to the error controller) no resource,

the reason why I got to the error controller it seems is I still got to the projects/index page. in the index action, I assumed that the user is logged in. But when it tries to get the logged in user

$user = \Zend_Auth::getInstance()->getIdentity();

It fails ...

How can I have redirectors working in Zend Test? Or maybe its not a problem with redirectors?

Was it helpful?

Solution

It's a two-part problem. First, the redirector by default calls PHP's exit after redirecting, which causes Zend_Test to cease execution. In your tests, you have to configure the redirector not to do that. Something like this:

$redirector = new Zend_Controller_Action_Helper_Redirector();
if (APPLICATION_ENV == 'testing') {
    $redirector->setExit(false);
}
$redirector->gotoUrl("/blah/blah");

But the problem in Controller Plugins is that after using a redirector, there's no way to prevent Zend Framework from going into the dispatch loop and trying to execute an action method anyway. I've read in various form posts (can't remember where offhand) that this is a known issue in Zend Framework which the developers are planning to address. For now, I work around this by adding a method like this into my Error Controller:

public function pluginRedirectorAction() {
    $this->_helper->layout()->disableLayout();
    $this->_helper->viewRenderer->setNoRender();

    $code = $this->_getParam('code');
    $uri = $this->_getParam('uri');

    if (APPLICATION_ENV == 'testing') {
        $this->_helper->redirector->setExit(false);
    }
    $this->_helper->redirector->setCode($code);
    $this->_helper->redirector->gotoUrl($uri);
}

Then in my controller plugins, I have a custom method for calling a redirect:

protected function redirect($code, $uri) {
    $redirector = new Zend_Controller_Action_Helper_Redirector();

    if (APPLICATION_ENV == 'testing') {
        $request = $this->getRequest();
        $request->setModuleName('default');
        $request->setControllerName('error');
        $request->setActionName('plugin-redirector');
        $request->setParam('code', $code);  
        $request->setParam('uri', $uri);

        $redirector->setExit(false);
    }

    $redirector->setCode($code);
    $redirector->gotoUrl($uri);
}

By doing this, you move the actual call to the redirector into the controller layer of your app, which enables unit testing to work properly (a.k.a. $this->assertRedirectTo('/blah/blah');.) This modifies the request to point to the pluginRedirectorAction() method in your Error Controller shown above. Redirects within your controller plugins are now called like this:

return $this->redirect(307, '/somewhere/else');

But it won't work from within the routeStartup() method because ZF fires up the router right after that, which will override the request parameters that the redirect() method specified. You'll have to rework the plumbing of your plugin to call your redirects from routeShutdown() or other methods that are called even later in the dispatch cycle. (I've only tested this within routeShutdown().)

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top