PHPSpec 101

Welcome to another episode of MageCast.io, in this series we will look at PHPspec and how we can use PHPspec to enhance our development workflow.

What is PHPspec?

Let's start by quickly introducing PHPspec, PHPSpec is a development tool from the xUnit family designed to help you write clean and working PHP code by using a technique derived from Test First development called (spec) behavior drive development.

This technique is also sometimes referred as SpecBDD because we primarily start describing the behaviour of our code before we write any part of it.

Strictly speaking there is no real difference between SpecBDD and TDD; both provide the same set of core features Moreover, concepts; however, SpecBDD changes the language we use to define and describe or testing scenarios.

SpecBDD and PHPspec for that matter, main goal is to keep focus on the behavior and design rather than in the verification and structure of the inner workings of a giving piece of code.

Getting our hands dirty

Now that we have a better understanding of what Phpspec is and how is suppose to work, let's fire up our terminal, get our hands dirty and see what PHPspec is capable of.

For this example, we assume that you have a working composer installation, and that you are comfortable working with it in the terminal if this not the case, please visit getcomposer.com before proceeding.

In this screencast, we will concentrate on introducing PHPspec, and it is workflow and such we will not dive into Magento specific code yet, our target is to create a simple example class.

Note: You will need composer installed on your system If not please visit getcomposer.com before proceeding.

Let's create a new project and a new composer file, we will use composer to install phpspec and any other libraries we might need.

1 {
2    "require-dev": {
3         "phpspec/phpspec": "~2.1"
4     },
5     "config": {
6         "bin-dir": "bin"
7     }
8 }

This small change in configuration will allow us to run phpspec with ease, and from the root from our project. Without phpspec will be installed in the default composer vendor path.

Moreover, now let's run composer install and let it do its magic, and to confirm everything is working we can run the the following command:

$ bin/phpspec run

As we can see that phpspec was installed correctly and working as expected.

Our first spec

Now that PHP spec is installed and working we can go ahead and describe one our classes, yes I said describe; as we said before PHPspec is part of the SpecBDD family, the main difference from a traditional TDD approach is that we will be describing our code public behavior.

The corresponding command is aptly named describe, so let's go ahead and describe an example Product class:

$ bin/phpspec describe Magecast/Store/Product

Moreover, let's follow up that by running phpspec again

$ bin/phpspec run

Code generation is one of the great features of Phpspec that people don't mention enough, let's say yes and let phpspec create the class for us, hmm wait there seems to be a problem since phpspec is asking to generate the class again for us; let's reply no this time and try to figure out what happened.

The problem

What happened? Well, we missed a critical step while configuring our extension; we forgot to specify the namespace, so phpspec does not know where too look when trying to load our Product.php class, fortunately, this is very simple to resolve.

Go ahead and open your composer.json file and change it as follows:

 1 {
 2     "require-dev": {
 3         "phpspec/phpspec": "~2.1"
 4     },
 5     "config": {
 6         "bin-dir": "bin"
 7     },
 8     "autoload": {
 9         "psr-0": {
10             "Magecast\\Store": "src"
11         }
12     }
13 }

We will also need to update the composer autoloader cache by running:

$ composer dump-autoload

Moreover, now if we run Phpspec again we should see our first spec passing.

Quick Recap

So not only Phpspec was smart enough to create the test class for us but it also generate our test subject class, by that we mean the class we are testing, as you can see this is a very powerful workflow, one I personally enjoy to great length.

If you where paying close attention, you probably notice that Phpspec did something else for us; it generated an example spec for, let's take a closer look at what this test is doing.

$ vim spec/Magecast/Store/ProductSpec.php

itisinitializable() is one of the internal Phpspec functions for now the only important thing to know is the this function checks for the existence of our subject class.

Describing Behavior

Since we have the Spec class open let's add some additional test, in a spec class all test functions must start with the it or its prefixes; so following the SpecBDD approach we are going to describe the Product class behavior.

If we think about it for a second how should our product behave?

  • It should have a name
1 <?php
2 function it_should_have_a_name() {
3     $this->getName()->shouldReturn('TestProductName');
4 }
  • It should have a sku
1 <?php
2 function it_should_have_a_sku() {
3     $this->getSku()->shouldReturn('12345');
4 }

Before we keep moving forward let's take a closer look at what the tests are doing. Many people get confused at first with the use of $this inside the Spec class, when the test executed this is actually referencing a reflection of the subject class.

Let's go ahead and run phpspec again:

$ bin/phpspec run

moreover, again Phpspec code generation features to the rescue, is not only detecting that Product methods haven't been implemented but is also offering to generate them for us, to which we are going to, of course, say yes.

Running phpspec again will return one passing test and two pending ones, so let's go ahead and open our Product class and implement some logic.

As we can see the class has two dummy functions that where generated by PHPspec.

In a real world scenario we more than likely would want to retrieve the name and sku values from some sort of storage like a database but for the purpose of this screencast we are going to store them as class properties and set them through the class constructor.

Our first step will be to implement the function logic for each of our methods, getName will return the property name and sku will return the sku class property.

We will also need to create our class construct, in this case, the constructor will take two variables the name and the sku and assign them to the matching class properties.

Moreover, now we run phpspec one more time and .... perfect we have 2 failing tests. Since phpspec is not setting any values when creating the class reflection name and sku will default back to empty strings, in fact the phpspec errors are actually giving us a great level of detail on what happened and why the test failed.

In true TDD fashion, we have a failing test so now we can move and create make the test pass in this case is as simple as passing the required values to our Product class constructor. This is also the perfect time to introduce two phpspec functions, let() and beConstructedWith.

First let's take a look at let(), for those familiar with Phpunit you can think of the let() function as the setUp() equivalent, it will run before each test scenario and is a convenient way working with constructors or doing any kind of setup before the test runs.

1 <?php
2 function let()
3 {
4 }

Inside our let() method we will be using the beConstructedWith() to pass the required values. Like so:

beConstructedWith is one of the internal phpspec functions that let us interact with our subject class constructor, for now the only important part to understand is that it will take any variables and pass them to the subject class constructor during the reflection phase.

1 <?php
2 function let() {
3     $this->beConstructedWith('TestProductName', '12345');
4 }

Finally, if we run phpspec one more time we should see only green and all our tests passing.

Final Recap

With this, we conclude our introduction to PHPspec, as we learn PHPspec is a great tool that brings new life and a fresh an approach to TDD in PHP.

We also learned that:

  • PHPSpec is easy to setup.
  • It writes the specifications for objects.
  • It allows to test in isolation through mocking.

In this first screencast, we have only touched the surface of PHPSpec, in the upcoming screencasts we will explore some of the most complex parts of PHPSpec in detail and how they relate with Magento Development.


comments powered by Disqus
Comment Permalink