Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ZF2 unit-testing authentication

I was learning about unit testing and I attempted to resolve the following issue:

Zend\ServiceManager\ServiceManager::get was unable to fetch or create an instance for zfcUserAuthentication

... using the only answer given at:

Simple ZF2 Unit Tests for a controller using ZfcUser

So my setUp function looks the same. Unfortunately, I get the error message:

Zend\Mvc\Exception\InvalidPluginException: Plugin of type Mock_ZfcUserAuthentication_868bf824 is invalid; must implement Zend\Mvc\Controller\Plugin\PluginInterface

It is caused at this part of the code (split up in my code in the same way):

$this -> controller->getPluginManager()
->setService('zfcUserAuthentication', $authMock); // Error refers to this line.

The $authMock object is apparently not implementing plugininterface, which I need to implement to pass into setService.

Is $authMock not meant to be passed there for it's use in unit testing? Should I be using a different (unit-testing oriented) setService method?

I need a way to handle logging into my application, or my unit testing is pointless.

Thanks for any advice.

=== Edit (11/02/2013) ===

I wanted to focus on this part for clarification, as I think this is the problem area:

// Getting mock of authentication object, which is used as a plugin.
$authMock = $this->getMock('ZfcUser\Controller\Plugin\ZfcUserAuthentication');

// Some expectations of the authentication service.
$authMock   -> expects($this->any())
    -> method('hasIdentity')
    -> will($this->returnValue(true));  

$authMock   -> expects($this->any())
    -> method('getIdentity')
    -> will($this->returnValue($ZfcUserMock));

// At this point, PluginManager disallows mock being assigned as plugin because 
// it will not implement plugin interface, as mentioned.
$this -> controller->getPluginManager()
->setService('zfcUserAuthentication', $authMock);

If the mock doesn't handle necessary implementations, how else am I to pretend to login?

like image 438
Shoreline Avatar asked Feb 10 '13 19:02

Shoreline


1 Answers

You have a problem with name-spacing or your autoloader.

When you are creating your mock, the class definition of ZfcUser\Controller\Plugin\ZfcUserAuthentication is not being found. So PHPUnit creates a mock that only extends this class for your test. If the class was available then PHPUnit will use the actual class to extend when making its mock, which will then use the parent classes/interfaces.

You can see this logic here: https://github.com/sebastianbergmann/phpunit-mock-objects/blob/master/PHPUnit/Framework/MockObject/Generator.php

    if (!class_exists($mockClassName['fullClassName'], $callAutoload) &&
        !interface_exists($mockClassName['fullClassName'], $callAutoload)) {
        $prologue = 'class ' . $mockClassName['originalClassName'] . "\n{\n}\n\n";

        if (!empty($mockClassName['namespaceName'])) {
            $prologue = 'namespace ' . $mockClassName['namespaceName'] .
                        " {\n\n" . $prologue . "}\n\n" .
                        "namespace {\n\n";

            $epilogue = "\n\n}";
        }

        $cloneTemplate = new Text_Template(
          $templateDir . 'mocked_clone.tpl'
        );

So if there is no class or interface, PHPUnit will actually create one itself so that the mock will meet the type hinting of original class name. However, any parent classes or interfaces will not be included because PHPUnit is not aware of them.

This would be due to not including the proper namespace in your test or having a problem in your autoloader. It is difficult to tell without actually seeing the entire test file.


Alternatively rather than mocking ZfcUser\Controller\Plugin\ZfcUserAuthentication, you could mock the Zend\Mvc\Controller\Plugin\PluginInterface in your test and pass that into the plugin manager. Though if you are type-hinting for the plugin in your code, your test still won't work.

//Mock the plugin interface for checking authorization
$authMock = $this->getMock('Zend\Mvc\Controller\Plugin\PluginInterface');

// Some expectations of the authentication service.
$authMock   -> expects($this->any())
    -> method('hasIdentity')
    -> will($this->returnValue(true));  

$authMock   -> expects($this->any())
    -> method('getIdentity')
    -> will($this->returnValue($ZfcUserMock));

$this -> controller->getPluginManager()
->setService('zfcUserAuthentication', $authMock);
like image 189
Schleis Avatar answered Nov 20 '22 08:11

Schleis