PSSMockeryBundle + PHPSpec: The Automation Of Mocking Services Begins!

Let’s say we want to spec a service we are trying to design and we want to use PHPSpec in order to do that.
Our system runs supported by the Symfony2 Components/Framework and according to the rules of the Dependency Injection game we have a client running the application from where we can get all the services of the project the container holds.
Since our service is meant to live within this “Sitz im Leben” or context and would need some interaction with other services we would like to develop the service classes right without leaving at any time this environment.

Welcome to PSSMockeryBundle world. Thanks to Jakub Zalas creator of PSSMockeryBundle, we have something that was meant to solve the problem indicated above by setting up a base class for the container in which we can easily mock any service on the container. Tie this with PHPSpec and you are about to discover a framework setup in which anyone can easily start creating, stubbing, mocking and perfecting their service design for their bundles and craft carefully their dependencies on other services.

<?php
namespace Acme\DemoBundle\Spec\Model;
 
use Symfony\Bundle\FrameworkBundle\Spec\WebSpec;
use Acme\DemoBundle\Model\MyService;
use Twig_Environment;
 
class DescribeMyService extends WebSpec
{
    protected $container;
    protected $client;
    protected $serviceUnderSpec;
 
    public function before()
    {
        $this->client = self::createClient();
        $this->container = $this->client->getContainer();
        $this->serviceUnderSpec = $this->spec(new MyService($this->container));
    }
 
    /**
     * Specking MyService class mocking another service directly related
     */
    public function itShouldGetCreateDirectoryUsingFileSystemServiceFromContainer()
    {
        $filesystemMock = $this->container->mock('filesystem', 'Symfony\Component\Filesystem\Filesystem');
        $filesystemMock->shouldReceive('mkdir')->once()->andReturn(true);
 
        $this->serviceUnderSpec->methodUsingFileSystem()->should->beTrue();
    }
 
    /**
     * Specking MyService method that uses the router service
     * we do not mock the routing but we do mock the Twig_Environment service
     * used by the router
     */
    public function itShouldIndirectlyMockServiceTheRouterDependsOn()
    {
        $twigEnvironmentMock = $this->container->mock('twig','\Twig_Environment');
        $this->serviceUnderSpec->methodUsingRouter()->should->be('/');
    }
}

We first have created a Spec class to speck our new service `MyService` non-existent at this time. The idea is to write one example method at a time. Here we have just gone ahead and written the two we are interested in demoing. Then we proceed to run:

~ phpspec src/Acme/DemoBundle/Spec/

Then we get an error telling us to create the methods on our empty class:

~ phpspec src/Acme/DemoBundle/Spec/
EE
 
Exceptions:
 
  1) Acme\DemoBundle\Spec\Model\MyService should get create directory using file system service from container
     Failure\Exception: $this->runExamples($exampleGroup, $reporter);
     ErrorException: Warning: call_user_func_array() expects parameter 1 to be a valid callback, class 'Acme\DemoBundle\Model\MyService' does not have a method 'methodUsingFileSystem' in /home/cordoval/sites-2/spec-symfony/vendor/phpspec/src/PHPSpec/Specification/Interceptor/Object.php line 66
 
  2) Acme\DemoBundle\Spec\Model\MyService should run command on client
     Failure\Exception: $this->runExamples($exampleGroup, $reporter);
     ErrorException: Warning: call_user_func_array() expects parameter 1 to be a valid callback, class 'Acme\DemoBundle\Model\MyService' does not have a method 'methodUsingRouter' in /home/cordoval/sites-2/spec-symfony/vendor/phpspec/src/PHPSpec/Specification/Interceptor/Object.php line 66
 
Finished in 0.052649 seconds
2 examples, 2 exceptions

Next is writing just about enough code on our Service class to make the examples pass. For that we take a look at our `MyService` class:

<?php
namespace Acme\DemoBundle\Model;
 
class MyService
{
    protected $container;
 
    public function __construct($container)
    {
        $this->container = $container;
    }
 
    public function methodUsingFileSystem()
    {
        $filesystem = $this->container->get('filesystem');
        return $filesystem->mkdir('folder1');
    }
 
    public function methodUsingRouter()
    {
        $router = $this->container->get('router');
        return $router->generate('_welcome');
    }
 
    public function checkInternalResponse()
    {
        return true;
    }
}

If you fail to meet the requirements with the writing of your methods, don’t worry PHPSpec is here to guide you make that example green:

~ phpspec src/Acme/DemoBundle/Spec/
.F
 
Failures:
 
  1) Acme\DemoBundle\Spec\Model\MyService should run command on client
     Failure\Error: $this->serviceUnderSpec->methodUsingRouter()->should->be('/somepath');
     expected '/somepath', got '/' (using be())
     # ./src/Acme/DemoBundle/Spec/Model/MyServiceSpec.php:39

Finished in 0.060213 seconds
2 examples, 1 failure

If we get everything right after all we should get:

~ phpspec src/Acme/DemoBundle/Spec/ -fd -c -b
Acme\DemoBundle\Spec\Model\MyService
  should get create directory using file system service from container
  should run command on client
 
Finished in 0.074234 seconds
2 examples

Notice that the examples are simple and are meant for you to see how easy is to use PHPSpec with PSSMockeryBundle within Symfony. We are using the spec-symfony sandbox available in our PHPSpec repos. A lot of effort is being put these days in bringing powerful tools for BDD Spec type of BDD methodology into Symfony2. PHPSpecBundle is the leading bundle along these lines that will have commands to generate the skeletons required to practice good BDD Spec type on Symfony2. BDD Spec and BDD Scenario complement each other so that is why it is important to keep the and use them together because they are more powerful together than separate.

I thank the Lord Jesus Christ for all the help in completing this effort through good friends and very good willing people like Jakub who is an expert in symfony2. We brainstormed once and next day he had the bundle ready. This is a very interesting community accomplishment! bravo!

Symfony2 Event Manager: The Stand Alone Way

My first clearer read on event manager on symfony2 was the following.

This article explains how to use the symfony2 even dispatcher to create custom listener and event using the symfony2 event manager component. The blog is very clear and teaches one how to get started on the code for when working inside a symfony2 project.

The second read was this article was a bit old. Actually a year old and taught me a lot about how to work with the stand-alone component however it was kind of unreliable but ultimately very useful. I really liked the examples that they have provided, that helped me to understand how to plug the events, how to work with priorities, how to filter values when working with events, and how use notifyUntil for instance, among other things.

Thirdly, we reviewed the symfony2 documentation chapter titled internal components here.

Basically the model has these components:

The Event is an object subclassed that is passed to all listeners with mechanisms to access information objects regarding the event. “The moral of the story is this: when creating a listener to an event, the Event object that’s passed to the listener may be a special subclass that has additional methods for retrieving information from and responding to the event.”
The Dispatcher class, which is the master class that notifies with the event all the listener classes.
The Listerner class, which is the class who is instructed to listen to the dispatcher events and that can be sub-classed so to extend with some methods to port some objects or data meant to be looked upon or processed/changed. Optionally we can gather all Events into an Events class for documentation purposes and have that class to hold statically the names or strings constants for the event names.

Note: It is important to realize that Dispatcher class is not singleton therefore it has to be injected into my master class from where O use it.
Note: On a last note, there are two approaches for using the event manager: (1) default listener approach, (2) using subscribers.
To stand on the shoulders of dustin10, I asked him what is the difference between these and he answered:

“cordoval they are basically the same thing except a subscriber implemements a method that lets doctrine know which events to subscribe to if using a listener you would create a tag for each event.”

Although I am using it standalone I can get that the idea.

Master Class

This class is in charge of creating all the objects, receive dispatcher injected through DI, assign the listeners on the constructor or elsewhere at initialization. Master class is also in charge of creating the specializing events that give access to some of the objects to compute from/on, and dispatching on a particular event string the corresponding event object just created.

So let’s get to the hands-on part. Let’s try to design this for the tictactoe problem I am facing.

/**
 * Central Class from where dispatcher is used
 */
namespace Tictactoe\Game;
use Symfony\Component\EventDispatcher\EventDispatcher;
class GameMaster
{
    protected $dispatcher = null;
    protected $player = null;
 
    public function __construct(EventDispatcher $dispatcher)
    {
        $this->dispatcher = $dispatcher;
 
        $listener = new GameListener();
        $dispatcher->addListener(GameEvents::onFinishPlay, array($listener, 'onFinishPlay'));
 
        $this->player = new Player();
    }
 
    public function play () {       
        $event = new FilterGameEvent($this->player);
        $dispatcher->dispatch(GameEvents::onFinishPlay, $event);
    }
}

Events Class

This class is not mandatory and its only purpose is to hold all the documentation for the strings corresponding to the event channels used in solving the problem. The Events class is mostly used when there are multiple events that need clear documentation. The Events class will allow a developer to easily find the specific event channels the core can dispatch through. This file then becomes very handy for when we need to hack the core, that is extending it, without touching it. We can see this as our list of hooks that we can use to clamp our added functionality to the application.

/**
 * Events Class
 */
namespace Tictactoe\Game;
 
final class GameEvents
{
    /**
     * The event listener receives an Tictactoe\Game\FilterGameEvent
     * instance.
     *
     * @var string
     */
    const onFinishPlay = 'turnswitcher.action';
}

Listener Class

This class should be the keystone in comprising all the logic that should be decoupled from the main core or all the logic that will extend the functionality of the core. Other classes could be easily copy pasted but this one. This class is very customized. The other classes are just copied and adapted to specific problem, they are support classes, whereas the Listener class is the main class that will extend the functionality.

/**
 * Listener TurnSwitcher Class
 */
namespace Tictactoe\Game;
 
use Symfony\Component\EventDispatcher\Event;
 
class TurnSwitcherListener
{
    // ...
    public function onFinishPlay(GameEvent $event)
    {
        // do something like
        $player = $event->getPlayer();
        // extend here the functionality of the core
        // ...
    }
}

Event Class

This class’ main purpose is to provide access to specific objects or parameters that will be useful to our logic extending the core. This class just customizes the method to get certain objects and thereby can be adapted anytime to provide first hand access to the objects that are being read from the master class or that are being written onto the master class set of properties.

/**
 * FilterGameEvent class
 * allows access to the player object
 */
namespace Tictactoe\Game\Event;
 
use Symfony\Component\EventDispatcher\Event;
use Tictactoe\Game\Player;
 
class FilterGameEvent extends Event
{
    protected $player;
 
    public function __construct(Player $player)
    {
        $this->player = $player;
    }
 
    public function getPlayer()
    {
        return $this->player;
    }
}

I hope this makes clearer the Event Manager Pattern as used in symfony2. I will be updating this to correct some mistakes I have made and also I will apply the pattern to the tictactoe game and provide more details on how to install the symfony2 component to be used standalone. So far we have worked on making things understandable.