Mocking user identities in Zend Framework 2 action controller unit tests

When testing Zend Framework 2 controllers, you may find yourself wanting to test a path within an action that requires a user to be logged in. We do this using PHPUnit mock objects.

I use a standard base test controller inherited from PHPUnit_Framework_TestCase. The pertinent methods are shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
namespace Netsensia\Test;
 
use PHPUnit_Framework_TestCase;
use TestSuite\Bootstrap;
use Zend\Mvc\Router\Http\TreeRouteStack as HttpRouter;
use Zend\Http\Request;
use Zend\Mvc\MvcEvent;
use Zend\Mvc\Router\RouteMatch;
use Netsensia\Adaptor\Auth\UserSessionModel;
use Zend\Test\PHPUnit\Controller\AbstractControllerTestCase;
 
class NetsensiaControllerTest extends AbstractHttpControllerTestCase
{
    ...
 
    protected function mockLogin()
    {
        $userSessionModel = new UserSessionModel();
        $userSessionModel->setUserId(1);
        $userSessionModel->setName('Tester');
 
        $authService = $this->getMock('Zend\Authentication\AuthenticationService');
        $authService->expects($this->any())
                    ->method('getIdentity')
                    ->will($this->returnValue($userSessionModel));
 
        $authService->expects($this->any())
                    ->method('hasIdentity')
                    ->will($this->returnValue(true));
 
        $this->getApplicationServiceLocator()->setAllowOverride(true);
        $this->getApplicationServiceLocator()->setService('Zend\Authentication\AuthenticationService', $authService);
    }
 }

You’ll notice the mockLogin() method, which creates a mock authentication service and uses it to override the existing service. We tell this mock object to return the instance of a user identity of our choosing and to return true when asked if an identity exists. Then, anywhere in the code that is being tested, it will appear as if a user is logged in, assuming that the test for user existence is querying the authentication service returned by the service manager.

Within a controller test, we simply use it as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
use Zend\Http\Request;
use Netsensia\Test\NetsensiaControllerTest;
 
class LocaleControllerTest extends NetsensiaControllerTest
{
    ...
    ...
 
    public function testCanSwitchLocaleWhenLoggedIn()
    {
        $this->mockLogin();
        $result = $this->dispatch('/locale/fr_FR');
        $this->assertRedirectTo('/user/profile');
    }
}

{ 6 comments… add one }

  • Tim Fountain November 23, 2013, 2:13 pm

    Thanks, this was helpful.

    Can you explain the setController() stuff? What is it that would call that method? Why do you need to set the service in two different service locators?

    Reply
  • Vladimir November 28, 2013, 1:23 pm

    THanks for this, looking promissing
    But i can not see where you are setting this?
    protected function setController($controller, $controllerName)
    Is there a piece of code you missed to post? :)

    Reply
    • admin January 10, 2014, 8:32 pm

      Sorry, there was a lot of stuff in the example that was not necessary, just some copied and pasted code from the base test class that I lazily included. I have removed it now to make it clearer.

      Reply
  • Brian Strickland January 10, 2014, 8:08 pm

    Thank you! Just FYI to anyone reading this, if you are working with ZfcUser, you need to change the service to: ZfcUser\Authentication\Storage\Db instead of Zend\Authentication\AuthenticationService

    Reply
  • William June 1, 2014, 9:24 pm

    I am having trouble getting this to work. It seems that what is going in the pipe is not coming out at the other end, as when I run

    $auth = new AuthenticationService();
    $loggedInUser = $auth->getIdentity();

    in the controller I am trying to test, the getIdentity() function is returning a NULL value, and hasIdentity() is returning FALSE. It seems as if the mock service is not being used. Any thoughts?

    Reply
  • Chris June 2, 2014, 7:34 am

    Hi William,

    Inside the controller, you’ll need to use something like:

    $auth = $this->getServiceLocator->get(‘Zend\Authentication\AuthenticationService’);

    Otherwise, you are just creating a new instance of AuthenticationService rather than using the one that was mocked and added to the Service Locator in the test case.

    Chris

    Reply

Leave a Comment