This is the second part of a series about Outside-in Behaviour Driven Development in PHP. The first part introduces outside-in development, and how to execute scenarios with Behat. Read this to catch up with the tools and the example we’ve used so far, then come back to find out how PHPSpec fits into this picture.

PHPSpec is the first ever PHP BDD framework. It is a port of RSpec to PHP created back in 2007 by Pádraic Brady and Travis Swicegood. Development in this framework stopped for a while and was reignited last August (2010). PHPSpec can be installed via pear, using these commands:

If you are accustomed to unit testing, here is a quick translation sheet for xUnit/xSpec terms:

  • In xUnit we test; in xSpec we describe, so your class names begin with “Describe”
  • In PHPSpec, the spec file for a class MyClass is named MyClassSpec.php
  • In xUnit we group tests in a TestCase. In xSpec we have examples that are grouped into Contexts
  • In xUnit each method is a test with the prefix test. In xSpec each method is an example, we use the prefix it
  • In xUnit we assert that something work as expected. In xSpec we specify how it should work

We’ve used Gherkin and Behat to specify how our application is supposed to work. We use PHPSpec to specify the behaviour of our classes. To get you started, let’s see a simple example showing this. We are building a class that greets the user with “Hello, World!”. The GreeterSpec.php file would look like this:

If you already have experience with Unit Testing, the above would look familiar to you. Note that instead of saying: $this->assertEquals('Hello, World!', $message) we say $message->should->be('Hello, World!'). We are describing how we want the behaviour to be, rather than testing it. The before() method is a setup method that would get run before any example is run; the spec() method is a decorator that wraps the object being tested so we call the expectations on the properties and methods results.

To run the spec we use the phpspec command:

First we run it and watch it fail. Then we write a Greeter class to satisfy the example.

PHPSpec and MVC

Going back to our Video Renting application. The newly released version (1.2beta) includes integration with Zend Framework which enables us to test the MVC (Model, View, Controller) components individually. When we test an MVC application, we should start with the view, because that’s what our scenario describes:

The text “Revolution OS” was not found anywhere in the text of the current page

Let’s keep all our specs in a folder called “spec”. To write a view spec we first add a folder called views under the spec folder. In our example, this is a view that corresponds to the index action of the review controller. We need to create an IndexSpec.php for our index.phtml view, containing the following code:

Notice that we namespaced our class with the name of the controller. In a modular Zend Framework application you would namespace with the module name and then controller name, e.g. ModuleController.

If we run the spec we should see an error because we haven’t met its description yet.

At this stage we need to create the view to deal with the error we saw. In this example, we will use Zend Tool, which creates the controller/action/view all in one go:

Now instead of an error, our output shows that we have a failure:

This output means that the view now exists, but it’s not showing the described text. That’s what we expect, since we haven’t coded the view yet. Let’s add some code to do so:

It should now pass.

That’s progress! However Behat is still not happy, because our controller is not setting the model for the view yet. Let us turn our focus to the controller, and start by creating a controllers directory and adding a ReviewControllerSpec.php.

DescribeReviewController must extend PHPSpecContextZendController. We also need to specify that our controller will send be accessed by POST and will create the model in it. The controller/action will be routed from “/review” and we can add an example to make sure the route work as expected.

We’ll also want to add the code for the controller itself. Assuming you are using Yadif or another IoC (Inversion of Control) container, it would look like this:

We’ll see that this now passes, when we run:

This looks much healthier, but if we run Behat, it will still be unhappy. We need to pass real data to the view, and then Behat will point out that the next step now is to describe the model. We need to have the data of our selected movies so we can fetch our view properly. Testing the models should be focused on behaviour, rather than the mechanics of database access. We know Zend_Db works, we need to know if we have made mistakes in our model. We will therefore test for the validation, filtering and business rules that we keep in the model.

We need to implement a isValid() method in the Video model. PHPSpec does not have a beValid matcher, but it will use predicate matchers and find a method isValid() by magic. Once the isValid() is implemented properly than the spec passes. You can do something similar if the form validation is stored in your (zend) forms. Also note the before() method that will be called before any example is run.

You can describe your mapper’s behaviour by inspecting that it calls the Data Access Object (DAO) to fetch to or persist data from the models. At that point you can then add the DAO (e.g. DbTable, Rss, etc).

Should we Hit the Database?

In many cases you simply need to verify the business logic and not the database operation. Hitting the database consumes both time and resources, which will slow down the execution. There will be times where we need to expose some database behaviour or just feel more confident that our models work. In those cases we can use a Test Data Builder pattern (this is a topic on its own, and I will save it for another post).

If you run Behat again after the model specs pass, the view should be displaying the correct data, so your scenario should pass, and a new scenario will be failing. We use Behat to tell us what to do next, and so the outside-in cycle is begins again.

Final Thoughts

In BDD, developers are driven by the specification, rather than tests of things they haven’t written yet. That said, the goals of both TDD and BDD are basically the same: making sure the user gets what they want. BDD makes that focus more explicit, and uses a language that invites the user to write their tests as a specification.

Behat and PHPSpec sit at different levels in the Outside-in cycle. Behat provides the outermost layer, allowing the stakeholders and developers to collaborate. PHPSpec provides the inner layer, allowing the specification of how the classes will collaborate with each other.