PHPSpec Advance

This post is inspired in the development here.

I received great help from jbohn.

So we are starting according to certain scenario.

Feature: Member rents video
  As a video-club member
  I want to rent a video
  So that I can take it away with me and watch it conveniently at home
Feature: Members rents video
  As a video-club member
  I want to rent a video
  So that I can take it away with me and watch it conveniently at home
  Scenario: Renting single video from oldies section
    Given I am in the "review" page
      And the selected video is "Revolution OS"
     When I click on "Rent"
     Then "£2" is added to my total
  Scenario: Renting 3 videos from oldies promotion
    Given I am in the "review" page
      And "Revolution OS" is selected
      And "Blade Runner" is selected
      And "The Wall" is selected
     When I click on "Rent"
     Then "£5" is added to my total

Then we run behat on the file and we are getting:

There is an error with the view already. The reason why we have passed the first step is only because there is already a router for the framework and debugging enabled. However the page that is gotten back by the step is not really a valid page. Once we are at this point we go deeper into the PHPSpec cycle now.

We run the following command because phpspec installed via pear is not the one we are using. Why? Because it does not have knowledge of our updates and also it does not have knowledge of our third party libraries, and also because we want to have full control on what gets loaded. I ran into a redefine issue with function any() conflict between phpunit and hamcrest library.

So here is the command I use:

~/sites-2/FormModelProjectBundle (phpspec) vendor/phpspec/scripts/phpspec.php vendor/bundles/Cordova/Bundle/FormModelBundle/

The `phpspec` script is smart enough to look for the right Spec folder under the bundle and runs the phpspec for IndexSpec.php which is made locatable because of its Spec.php ending.

The resulting output of the previous command is:

  1) Cordova\Bundle\FormModelBundle\Specs\views\Index renders the selected video
     Failure\Exception: $this->runExamples($exampleGroup, $reporter);
     InvalidArgumentException: Unable to find template "CordovaFormModelBundle:index.html.twig".
Finished in 0.134287 seconds
1 example, 1 exception

Notice here that it tells us that we have the wrong path for our template. Which theoretically is unimplemented. The exact error means that we actually need to insert the logical controller name between the ::. This is familiar to sf2 developers. So we do the change and having a lame default controller we have now the following error:

  1) Cordova\Bundle\FormModelBundle\Specs\views\Index renders the selected video
     Failure\Error: $output->should->contain('Revolution OS');
     expected to contain 'Revolution OS', found no match (using contain())
     # ./vendor/bundles/Cordova/Bundle/FormModelBundle/Specs/views/IndexSpec.php:16

Finished in 1.080713 seconds
1 example, 1 failure

Notice: we could have used the generation of a bundle. However since controllers are mostly custom code then there is no point really in using generator unless it is a new action to be placed in a new bundle. So there is no need in sf2 to use a generation command here unless in the case where we are putting things into a new bundle. For all other cases we just copy/paste a controller common template.

Now we know where all our efforts must concentrate, namely here $output->should->contain('Revolution OS');. This means the template rendered has nowhere the label `Revolution OS`. The major problem here is that we need to work on the loading of matchers for Symfony2.

Once we worked on the matcher `Contain` we can run the phpspec command again and we get:

vendor/phpspec/scripts/phpspec.php vendor/bundles/Cordova/Bundle/FormModelBundle/Specs/views/IndexSpec.php
Finished in 1.029784 seconds
1 example

Now we are ready to head onto developing the C standing for the controller layer. The controller basically is in charge of doing the dispatching of things. So we need to work again on the Symfony2 controller version of things.

class DescribeIndex extends View
    function itRendersTheSelectedVideo()
        $video = \Mockery::mock(
            'Application_Model_Video', array('getName' => 'Revolution OS'));
        $output = $this->render('MyBundle:Video:detail.html.twig', array(
            'video' => $video,
        $output->should->contain('Revolution OS');
use PHPSpec\Context;
use Symfony\Bundle\FrameworkBundle\Templating\EngineInterface;
class View extends Context
    private $engine;
    public function setTemplatingEngine(EngineInterface $engine)
        $this->engine = $engine;
    public function spec()
        $interceptor = call_user_func_array(
            'create'), func_get_args()
        $interceptor->addMatchers(array('contain', 'haveSelector'));
        return $interceptor;
    public function render($template, array $templateContext = array())
        if (is_null($this->engine)) {
            throw new \LogicException('No templating engine set.');
        return $this->spec($this->engine->render($template, $templateContext));

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>