Welcome to Abdul Malik Ikhsan's Blog

Testing Zend Framework 2 factory using Prophecy

Posted in Tutorial PHP, Zend Framework 2 by samsonasik on August 29, 2015

Simulate every detail what classes methods doing with phpunit sometime make you little crazy and make you do too many effort ? There is a library to reduce that! It named Prophecy, a “Highly opinionated mocking framework for PHP 5.3+”.

In this post, I want to show you in Zend Framework 2 application case, which if we are using ZF2 ~2.4.0, we already have zendframework/zend-test which require phpunit/phpunit:~4.0. The Prophecy is included in phpunit start from 4.5, so the requirement already met. If we use old ZF2 version, we can add "phpspec/prophecy-phpunit": "~1.5" under require-dev in our composer requirement.

For example, we have a factory class for controller creation like the following:

namespace MyModule\Factory\Controller;

use MyModule\Controller\MyController;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;

class MyControllerFactory implements FactoryInterface
{
    /**
     * @{inheritDoc}
     */
    public function createService(ServiceLocatorInterface $sl)
    {
        $services           = $sl->getServiceLocator();
        $formElementManager = $services->get('FormElementManager');
        $myForm             = $formElementManager->get('MyModule\Form\MyForm');
        
        $controller = new MyController($myForm);

        return $controller;
    }
}

Without Prophecy, we need write the tests like the following:

namespace MyModule\Factory\Controller;

use PHPUnit_Framework_TestCase;
use MyModule\Factory\Controller\MyControllerFactory;

/**
 * @author Abdul Malik Ikhsan <samsonasik@gmail.com>
 */
class MyControllerFactoryTest extends PHPUnit_Framework_TestCase
{
    /** @var MyControllerFactory */
    protected $factory;

    /** @var ServiceLocatorInterface */
    protected $serviceLocator;

    /** @var \Zend\Mvc\Controller\ControllerManager */
    protected $controllerManager;

    public function setUp()
    {
        $this->controllerManager = $this->getMockBuilder('Zend\Mvc\Controller\ControllerManager') 
                                        ->disableOriginalConstructor()
                                        ->getMock();
        $this->serviceLocator    = $this->getMock('Zend\ServiceManager\ServiceLocatorInterface');

        $factory = new MyControllerFactory();
        $this->factory = $factory;
    }

    public function testCreateService()
    {
        $formElementManager = $this->getMockBuilder('Zend\Form\FormElementManager')
                                   ->disableOriginalConstructor()
                                   ->getMock();
        $myForm  = $this->getMockBuilder('MyModule\Form\MyForm')
                                   ->disableOriginalConstructor()
                                   ->getMock();
        $formElementManager->expects($this->once())
                           ->method('get')
                           ->willReturn($myForm);
        $this->serviceLocator->expects($this->once())
                             ->method('get')
                             ->with('FormElementManager')
                             ->willReturn($FormElementManager);
        
        $this->controllerManager->expects($this->once())
                                ->method('getServiceLocator')
                                ->willReturn($this->serviceLocator);

        $result = $this->factory->createService($this->controllerManager);
        $this->assertInstanceOf('MyModule\Controller\MyControllerFactory', $result);
    }
}

This is the example for 1 dependency, we can to call expects($this->once()), what if we have many dependencies which use many services?, we can end up doing something like this:

$myService1 = $this->getMockBuilder('MyModule\Service\MyService1')
                   ->disableOriginalConstructor()
                   ->getMock();
$this->serviceLocator->expects($this->at(0))
                     ->method('get')
                     ->with('MyService1')
                     ->willReturn($myService1);
$myService2 = $this->getMockBuilder('MyModule\Service\MyService2')
                   ->disableOriginalConstructor()
                   ->getMock();
$this->serviceLocator->expects($this->at(1))
                  ->method('get')
                  ->with('MyService2')
                  ->willReturn($myService2);

And if we added a service dependency in the middle, we need to reset the increment.

With Prophecy, it now simplified, we can eliminate them, and use the mock just like the object do, like the following:

$formElementManager = $this->prophesize('Zend\Form\FormElementManager');
$myForm             = $this->prophesize('MyModule\Form\MyForm');
$formElementManager->get('MyModule\Form\MyForm')->willReturn($myForm);

Let’s see the full code for testing factory above if we are using Prophecy:

namespace MyModule\Factory\Controller;

use PHPUnit_Framework_TestCase;
use MyModule\Factory\Controller\MyControllerFactory;

/**
 * @author Abdul Malik Ikhsan <samsonasik@gmail.com>
 */
class MyControllerFactoryTest extends PHPUnit_Framework_TestCase
{
    /** @var MyControllerFactory */
    protected $factory;

    /** @var ServiceLocatorInterface */
    protected $serviceLocator;

    /** @var \Zend\Mvc\Controller\ControllerManager */
    protected $controllerManager;

    public function setUp()
    {
        $this->controllerManager = $this->prophesize('Zend\Mvc\Controller\ControllerManager');
        $this->serviceLocator    = $this->prophesize('Zend\ServiceManager\ServiceLocatorInterface');

        $factory = new MyControllerFactory();
        $this->factory = $factory;
    }

    public function testCreateService()
    {
        $formElementManager = $this->prophesize('Zend\Form\FormElementManager');
        $myForm             = $this->prophesize('MyModule\Form\MyForm');
        $formElementManager->get('MyModule\Form\MyForm')->willReturn($myForm);
        $this->serviceLocator->get('FormElementManager')->willReturn($formElementManager);
        $this->controllerManager->getServiceLocator()->willReturn($this->serviceLocator);

        $result = $this->factory->createService($this->controllerManager->reveal());
        $this->assertInstanceOf('MyModule\Controller\MyController', $result);
    }
}

That’s it ;). It now simplified.

Advertisements
Tagged with: ,

7 Responses

Subscribe to comments with RSS.

  1. Leandro Silva said, on August 29, 2015 at 9:21 pm

    Great post samsonasik!! Was about to read more about Prophecy with ZF2. Thanks!

  2. tmquang6805 said, on September 21, 2015 at 9:58 am

    May be you miss create $prophet property on setUp
    $this->prophet = new \Prophecy\Prophet();

    • samsonasik said, on September 22, 2015 at 2:41 am

      it’s not. phpunit ~4.5 already has integration with it.

  3. funman1801 said, on October 28, 2015 at 5:07 pm

    Greate post, thanks man, i like your post but why you mock ‘controllerManager’, ‘serviceLocator’?

    In my viewpoint, when we write test code we need focus on responsibility of system under test(component, class, method…).

    Dont’ mock type you don’t own https://github.com/mockito/mockito/wiki/How-to-write-good-tests

    • samsonasik said, on October 28, 2015 at 10:53 pm

      because ControllerManager need to get the serviceLocator, and serviceLocator needed to get the service, you need to read about zf factory architecture to know about that ๐Ÿ˜‰

      • funman1801 said, on October 29, 2015 at 10:22 am

        Yeah i know about zf factory architecture and I think in this test we don’t need use mock

        Other way to test MyControllerFactory responsibility:
        1. Get real ServiceLocator (from phpunit Bootstrap)
        2. [Option] ServiceLocator Set Form Instance for key ‘MyModule\Form\MyForm’ ( If ‘MyModule\Form\MyForm’ not exist when you write specification for MyControllerFactory)
        3. Inject real ServiceLocator to MyControllerFactory instance.
        4. Assert MyControllerFactory factory method return instance of ‘MyModule\Controller\MyController’.

        You have typo issue?
        this->assertInstanceOf(‘MyModule\Controller\MyControllerFactory’, $result);
        I think you mean:
        this->assertInstanceOf(‘MyModule\Controller\MyController’, $result);

      • samsonasik said, on October 30, 2015 at 12:04 am

        I usually use real servicelocator only for fixture tests, that’s ‘too heavy’ for me just to test factory, btw, i did typo indeed, that’s fixed ๐Ÿ˜‰


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: