PHPSpec now running specs on itself with Composer and Travis!

PHPSpec now run its own specification examples on itself with the help of composer and travis.
Composer is the tool to automate dependencies. PHPSpec now has a direct dependency on Mockery/Mockery and when installed through composer on can run the tests with `./scripts/phpspec-composer.php spec/` provided previously we have run `composer.phar install`. If you don’t know how to get the install step take a look at the earlier posts that teach you just that.

In order to have a solid stable version of phpspec that can be well tested or rather specified we used `travis` continuous integration service. With travis we are able to tell it to test phpspec specifications in any version of php and in any pre-configured environment.
This is the PR send that implements all the changes: https://github.com/phpspec/phpspec/pull/59/files.
And here is the output on travis:

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!

PHPSpec With Composer: The Short And Simple Story

We wanted to do something more with composer and so we dug further and found composer allows us to really simplify setup of our phpspec projects.
For our main project we create a composer.json on the root folder of our project as follows:

{
    "config": { "bin-dir": "." },
    "require": {
        "php": ">=5.3.2",
        "cordoval/phpspec": "master"
    }
}

What this will do is to install all dependencies so we can work with phpspec and its dependencies and also have a standalone script with the phpspec version of our like. In this case we are using the only version existent at the time of writing which is `master`. The script linking magic is accomplished by the composer.json in phpspec as shown here:

{
    "name": "cordoval/phpspec",
    "description": "PHPSpec: The BDD Specification Library",
    "keywords": ["bdd", "spec", "test"],
    "homepage": "http://phpspec.net",
    "license": "MIT",
    "authors": [
        {
            "name": "PHPSpec Team",
            "homepage": "https://github.com/phpspec/phpspec/contributors"
        }
    ],
    "bin": ["scripts/phpspec-composer.php"],
    "autoload": {
        "psr-0": { "PHPSpec\\": "src/" }
    },
    "require": {
        "php": ">=5.3.2",
        "cordoval/mockery": "master-dev"
    }
}

Of course you don’t need to write this anymore as it is taken care by us.

Instructions for installing the composer are easy. You can either create the composer.json on your personal project or clone this sample project.

git clone git://github.com/cordoval/phpspec-composer.git
cd phpspec-composer

The only thing besides writing the short composer.json above is to install composer. It is simple:

wget http://getcomposer.org/composer.phar
php composer.phar install

If you have composer installed as in a development setup like in a previous post you just do `composer install`.
The last command will install phpspec and all its dependencies included in the phpspec composer.json package specification, that is to say: Mockery.
Now run phpspec with:

~ ./phpspec-composer.php -h
Usage: phpspec (FILE|DIRECTORY) + [options]
 
    -b, --backtrace          Enable full backtrace
    -c, --colour, --color    Enable color in the output
    -e, --example STRING     Run examples whose full nested names include STRING
    -f, --formater FORMATTER Choose a formatter
                              [p]rogress (default - dots)
                              [d]ocumentation (group and example names)
                              [h]tml
                              [j]unit
                              custom formatter class name
    --bootstrap FILENAME     Specify a bootstrap file to run before the tests
    -h, --help               You're looking at it
    --fail-fast              Abort the run on first failure.
    --version                Show version

I thank the Lord Jesus Christ. And the opportunity to have received much help through helpful top guys like Stof, Seldaek, igorw and the developers of composer.

Load Behat CommonContexts from Symfony2

To load common contexts for behat on a symfony2 project we basically add to deps and also to autoload as follows:

// to deps
[BehatCommonContexts]
    git=https://github.com/Behat/CommonContexts.git
    target=/behat/CommonContexts
// to autoload.php
'Behat\CommonContext'       => __DIR__.'/../vendor/behat/CommonContexts',

Then do a `bin/vendors install` and then run your behat scenarios!

Updating Behat

I was working on redirect steps and found a command set to upgrade my behat environment.
You can check first what you currently have:

pear list -c behat

And then compare to the following:

sudo pear channel-discover pear.behat.org
pear remote-list -c behat
sudo pear channel-update pear.behat.org
sudo pear upgrade behat/behat
sudo pear upgrade behat/gherkin
sudo pear upgrade behat/mink

Sharing so you don’t have to look or spend time on this and keep to the current bleeding edge.

Capifony + Symfony2 Revisited Experience

Deploying with capifony is really an experience. Even though I had set it up after several months successfully and with the help of @weaverryan I struggled another few hours to get it work for my project.

I needed java installed for assetic and yui-compressor too so I downloaded from, you can select first option jdk and download the compressed binary for your system either 64 or 32:
JDK binary.
This is really just downloading the file and setting up PATH for the system to find the java/bin/java executable. Same applies to yuicompressor which can be downloaded from here.

Another thing I had to deal with was the permissions, basically I ended up setting umask(0000) before all my app*.php and app/console. Besides this I created a script ./script.sh that will remove rm -rf cache/* and logs/* since they are impediment to more capifony deploys after the first if they are to be shared folders. I had shared vendors, logs, cache and the parameters.ini.

In addition to this even though I had installed capifony the easy way, I ended up doing it again over git and that too gave me the opportunity to remove the –reinstall out of the bin/vendors install task which repeated every `cap deploy`. I compiled it with this new change and use it to my like, with faster deployments than ever!

Other things I have learned with capifony is that one can issue all symfony2 commands and custom commands by just typing cap symfony and then wait for the prompt so that one can enter any fos:user:create or any such sort of sf2 command that we can think of. This was very useful to warmup on demand, or to create users and promote them so that one could access client admin dashboard on prod server.

Yet another by-product of this resulted in trial and error kind of easy of the configuration to send emails with swiftmailer. It was a long jorney but I got it to send email via smtp.

Should you want to get straight A’s from the very beginning here is my code you can use to avoid hours of long setup:

set :application, "cordoval"
set :maindomain,  "sites.pe"
set :domain,      "#{application}.#{maindomain}"
set :deploy_to,   "/home/cordoval/www/#{domain}"
 
set :scm,        :git
set :scm_command, "/usr/bin/git"
set :scm_verbose, true
set :repository,  "#{maindomain}:/home/cordoval/repos/#{application}.git"
 
set :branch, "new-feature"
set :user, "cordoval"
set :password, 'jaris'
 
set :shared_files, ["app/config/parameters.ini"]
set :shared_children, [app_path + "/logs", web_path + "/uploads", app_path + "/cache", "vendor"]
set :update_vendors, true
set :dump_assetic_assets, true
 
role :web,        domain                         # Your HTTP server, Apache/etc
role :app,        domain                         # This may be the same as your `Web` server
role :db,         domain, :primary => true       # This is where Rails migrations will run

set :use_sudo,   false
set  :keep_releases,  3
ssh_options[:forward_agent] = true

Let the reader understand the code above.

Understanding Sismo Processes Part I

Update: By the comments from @stof I made exit($return_var) and system(…,$return_var) takes a second argument as shown. So it works now like it should have been. Thanks!

What am I trying to do? Yes I am trying to understand this process method with which sismo personal ci runs my behat tests. The issue here is because I am running a script and not a simple command the process seems to end simply in a green result whereas I could still have some steps failing while on behat but sismo would not want to color my commit with red.

So that said we give credits for the code below to SensioLabs. How then are we going to change this method to allow for our examination of behat results? passing or failing?

The problem:

  /**
   * Runs the process.
   *
   * The callback receives the type of output (out or err) and
   * some bytes from the output in real-time. It allows to have feedback
   * from the independent process during execution.
   *
   * The STDOUT and STDERR are also available after the process is finished
   * via the getOutput() and getErrorOutput() methods.
   *
   * @param Closure|string|array $callback A PHP callback to run whenever there is some
   *                                       output available on STDOUT or STDERR
   *
   * @return integer The exit status code
   *
   * @throws \RuntimeException When process can't be launch or is stopped
   *
   * @api
   */

So right from here we see there could be potential hooks from where we can insert our telling the system when it is an error or when it is a passing test/description/etc.

    public function run($callback = null)
    {
        $this->stdout = '';
        $this->stderr = '';
        $that = $this;
        $callback = function ($type, $data) use ($that, $callback)
        {
            if ('out' == $type) {
                $that->addOutput($data);
            } else {
                $that->addErrorOutput($data);
            }
 
            if (null !== $callback) {
                call_user_func($callback, $type, $data);
            }
        };
 
        $descriptors = array(array('pipe', 'r'), array('pipe', 'w'), array('pipe', 'w'));
 
        $process = proc_open($this->commandline, $descriptors, $pipes, $this->cwd, $this->env, $this->options);
 
        if (!is_resource($process)) {
            throw new \RuntimeException('Unable to launch a new process.');
        }
 
        foreach ($pipes as $pipe) {
            stream_set_blocking($pipe, false);
        }
 
        if (null === $this->stdin) {
            fclose($pipes[0]);
            $writePipes = null;
        } else {
            $writePipes = array($pipes[0]);
            $stdinLen = strlen($this->stdin);
            $stdinOffset = 0;
        }
        unset($pipes[0]);
 
        while ($pipes || $writePipes) {
            $r = $pipes;
            $w = $writePipes;
            $e = null;
 
            $n = @stream_select($r, $w, $e, $this->timeout);
 
            if (false === $n) {
                break;
            } elseif ($n === 0) {
                proc_terminate($process);
 
                throw new \RuntimeException('The process timed out.');
            }
 
            if ($w) {
                $written = fwrite($writePipes[0], (binary) substr($this->stdin, $stdinOffset), 8192);
                if (false !== $written) {
                    $stdinOffset += $written;
                }
                if ($stdinOffset >= $stdinLen) {
                    fclose($writePipes[0]);
                    $writePipes = null;
                }
            }
 
            foreach ($r as $pipe) {
                $type = array_search($pipe, $pipes);
                $data = fread($pipe, 8192);
                if (strlen($data) > 0) {
                    call_user_func($callback, $type == 1 ? 'out' : 'err', $data);
                }
                if (false === $data || feof($pipe)) {
                    fclose($pipe);
                    unset($pipes[$type]);
                }
            }
        }
 
        $this->status = proc_get_status($process);
 
        $time = 0;
        while (1 == $this->status['running'] && $time < 1000000) {
            $time += 1000;
            usleep(1000);
            $this->status = proc_get_status($process);
        }
 
        $exitcode = proc_close($process);
 
        if ($this->status['signaled']) {
            throw new \RuntimeException(sprintf('The process stopped because of a "%s" signal.', $this->status['stopsig']));
        }
 
        return $this->exitcode = $this->status['running'] ? $exitcode : $this->status['exitcode'];
    }

Zombie Pitfalls: Part III

I have been following this thread here and got some responses and experiences that I would like to share.

First there was a post from @nationalfieldtravis. Basically what he is proposing is to just run a zombie instance from outside the process which usually it is autostarted and run it manually. This is the sort of thing done with sahi that one has to run it before running the tests/features.

Second @FireFoxIXI posted a gist which I forked just to save a copy for myself here. The solution proposed is to just modify the driver connection code and do more retries through a parameter.

These two workaround don’t work perfectly and i still get the error when doing some ajax.

throw new Error("No OPTION '" + value + "'");

So I am still awaiting to find some time to first reproduce this error and then work together with behat team on a PR. This post is mainly to document and dont’ loose track of this effort with using zombie for speeding sahi feature running.

Sismo Setup As Personal CI: Behat + PHPSpec Powered!

Update notice: I was told by fabpot that: “The command can be any valid script. So, you can run whatever you want here. The best way is probably to create a script and call it as the command to run.”

I struggled several hours with Sismo but got it to do what I wanted.
I went in and configured a config.php file into .sismo/config.php introducing my project not as a github project but as a custom project since everything is local for me for this particular project. I also have it on github but I want to be able to *assert* the situation locally as we are developing with BDD.
So working on a particular branch of my project ‘composition’ I init bare a local repository in a repos folder ‘/home/cordoval/sites-2/repos/bdd-local.git’ and then assign the command to run phpspec on a folder Spec under the corresponding subproject.

<?php
$projects = array();
 
// create a DBus notifier (for Linux)
$notifier = new Sismo\DBusNotifier();
 
// add a project with custom settings
$sf2 = new Sismo\Project('bdd-local');
$sf2->setRepository('/home/cordoval/sites-2/repos/bdd-local.git');
$sf2->setBranch('composition');
 
// builds command for phpspec
$phpspecCommand = '/home/cordoval/sites-2/FormModelProjectBundle/vendor/phpspec/scripts/phpspec.php';
$phpspecFolder = '/home/cordoval/sites-2/FormModelProjectBundle/vendor/PHPAutotest/demo/PHPSpec/MineSweeper/Specs/';
$longCommand = 'find ' . $phpspecFolder . '*.php -exec ' . $phpspecCommand . ' \'{}\' -f documentation --color \\;';
 
// sets command, slug for web interface, links to individual commit, and notifier popup
$sf2->setCommand($longCommand);
$sf2->setSlug('bdd-local');
$sf2->setUrlPattern('http://sismo.local/bdd-local/commit/%commit%');
$sf2->addNotifier($notifier);
$projects[] = $sf2;
 
return $projects;

The setup for the phpspec and behat is custom and it is discussed for a later post. However, it is shown here to show that we are successfully using PHPSpec and Behat on our BDD design cycle with Sismo.

The git post-commit hook used is quoted here commented:

#!/bin/sh
#
# This hook script is called after a successful
# commit is made.

# sets the environment variables for sismo
. /home/cordoval/sites-2/sismo/sismoenv
 
# pushes the latest commit on the current branch whichever this is 
# up to the .git repository that sismo is cloning/fetching?
/usr/bin/git push local
 
# runs sismo with .sismo/config.php configuration (aka Behat, PHPSpec, PHPUnit, or other as sismo is tool agnostic)
/home/cordoval/sites-2/sismo/sismo --verbose build bdd-local `git log -1 HEAD --pretty="%H"` &

I added the push step in order to make it work since without this push step sismo would be cloning (and not sure if fetching after it clones in the first processing step) a repository out of sync since the last commit we made it is still on the working directory and has not been pushed upstream.

Result, it works like a charm.

And on a behat cycle:

I should acknowledge the brainstorming time with NordishByNature on the channels and overall to my God for providing me grace time for this. Currently listening …

Discussing how to BDD Symfony2 Router+URL Mapper

I got a response but still roughly thanks to Marcin Sikon
hmm, maybe this:

$container->get('router')->match('/review')
// result of this function is false or array with '_controller' and '_route',
 
_controller looks like 'Acme\Bundles\DemoBundle\Controller\DefaultController::indexAction'

now you must parse this string for get action and bundle name …

is there a way to have it already parsed through some component…
from kernel $container->get(‘kernel’) get registerBundles and find bundle matching to bundle namespace
any more ideas? anyone?

I got another response from Dmitry Bykadorov:

Finally I’m using this solution:

       $serviceContainer = self::$kernel->getContainer();
       $router = $serviceContainer->get('router');
       $crawler = $client->request('GET', $router-
>generate('admin_blogpost'));

- “Now I’m tesing both controller & routing )”

So in view of this we can do:

Open Letter On The Behat+PHPSpec Expand-Collapse Methodology

hi,

on another note, i saw your bowling example [_md's].

I see how behat is very good on one side at the high level. You probably could have designed the class without relying on PHPSpec.

HOWEVER, if you chose to use PHPSpec once you got the red on behat and then came back after you greened all your PHPSpec descriptions just to ensure some adding of scenarios for completeness then it is great.

I believe bowling example is not very clear. But I see two patterns.

When the logic behavior (methods/what it does) described in the scenarios matches closely the class and when scenarios really describe acceptance behavior at a high level and it is not close to class behavior because the logic behavior is not very clear at the moment. This has to do I think with separation of concerns.

When separation of concerns is not clearly laid out or is mixed or embedded neatly into a behavior then it is hard to describe behavior that matches class behavior of the methods. When separation of concerns seems to be easily described with particular scenarios then we realize that separation of concerns has already been decoupled into method-like logic behavior (bowling case).

When separation of concerns is coupled and cannot be decoubled at the high level which is in most cases then extra procedure is followed:

1. PHPSpec will here be more heavily used to attempt the decoupling of the behavior in expected functionality, more and more fine grained each time or at least the description will be able to extend past the behat scenarios and can then describe some borders and more shape to what the class does. This is in all separation of concerns and some more other design.

2. After PHPSpec passes and Class(es) is(are) implemented then next step is to put to the test the Class(es) so designed and behat will fulfill the role of integration scenario BDD process.

So PHPSpec helps with separation and splitting to define finer behavior whereas Behat starts putting things back together to orchestrate things split to a larger view back together.

Expand-Collapse/Separation-Reunion concept of Design which is how things are designed.

I am currently working on a minesweeper example that shows perhaps some more clear this process… not sure how far i can get but i will try to be more clearer hopefully. Great job _md, this is a great advance in understanding these great tools! Behat+PHPSpec FTW.

Please let me know your thoughts!

Zombie Pitfalls: Part II

I ran into a problem with connection refused (111) by zombie. And I was told by @havvg to do the following. This is adapted from @havvg gist. You can find his blog here too.

    zombie:
      auto_server: false

and

var net = require('net');
var sys = require('sys');
var zombie = require('zombie');
var browser = null;
var pointers = [];
var buffer = "";
 
net.createServer(function (stream) {
  stream.setEncoding('utf8');
  stream.allowHalfOpen = true;
 
  stream.on('data', function (data) {
    buffer += data;
  });
 
  stream.on('end', function () {
    if (browser == null) {
      browser = new zombie.Browser({ debug: true });
 
      // Clean up old pointers
      pointers = [];
    }
 
    eval(buffer);
    buffer = "";
  });
}).listen(8124, '127.0.0.1');
 
console.log('Zombie.js server running at 127.0.0.1:8124');

This works.

Another problem was that zombie was interacting with modernizr library:

"TypeError: Cannot set property cssText of [object Object] which has only a gettern    at Object.a (/js/libs/modernizr-1.7.min.js:2:2493)n    at Object.flexbox (/js/libs/modernizr-1.7.min.js:2:2595)n    at Object.<anonymous> (/js/libs/modernizr-1.7.min.js:2:7228)n    at Object.<anonymous> (/js/libs/modernizr-1.7.min.js:2:8964)n    in http://h.local/app_test.php/login"

I took away the library and of course it solved the problem.