DevOps Zone is brought to you in partnership with:

I am a programmer and architect (the kind that writes code) with a focus on testing and open source; I maintain the PHPUnit_Selenium project. I believe programming is one of the hardest and most beautiful jobs in the world. Giorgio is a DZone MVB and is not an employee of DZone and has posted 638 posts at DZone. You can read more from them at their website. View Full User Profile

Behavior-Driven Development in PHP with Behat

02.08.2011
| 15180 views |
  • submit to reddit

What is Behavior-Driven Development? A formal definition can be found on Wikipedia:

BDD is a second-generation, outside-in, pull-based, multiple-stakeholder, multiple-scale, high-automation, agile methodology. It describes a cycle of interactions with well-defined outputs, resulting in the delivery of working, tested software that matters.

A brief, imprecise definition of Behavior-Driven Development can be: an evolution of Test-Driven Development where specifications are built as sets of examples, which coincide with automated tests. These tests are written in something similar to natural language, usually following a Given/When/Then paradigm.

Note that this definition only cites what BDD adds on top of TDD: mocks usage for example is something I take for granted.

How it works, but most important: why

In BDD, specifications for the software are written as feature files, which is mostly natural language that follows some conventions. Every feature file contains several scenarios, which can be thought of as isolated tests.

Of course it's difficult for a tool like Cucumber or Behat to instantiate objects by interpreting natural language, so behind the feature file there is a configuration written in a programming language like PHP (Ruby in the case of Cucumber).

The configuration has to be written by a programmer; however, the feature files can be written and maintained by a domain expert or by any non technical person. In fact, they can even be written before the implementation and the configuration, like in Acceptance Test-Driven Development.

Personally, I interprete feature files as the specification: no more Word or PDF documents to discuss with analysts, but a readable, executable plain text files. If you check-in Word documents into your version control system like me, think about it.

Implementation

The feature files are just plain text. Behat, which is the tool explored in this article, says to save them as plain text files with the .feature extension.

The configuration I was talking about is a set of steps definitions, which is PHP code. Every step definition is a regular expression associated with a callback. Once the feature file is executed, every line would be analyzed and its interesting part will be matched with the regular expressions. The regex that matches define the callback to execute (it's simpler to show you an example than to describe it).

Finally, an environment must be predefined for scenarios: for example, these scripts will load classes, create objects and import assertions.

When a feature file is ready to be executed, you can run your tests from the comman line, with an utility called in fact behat that works almost like PHPUnit.You can install it via PEAR in a matter of seconds.

By the way, if you work with Vim, there is a simple way to get syntax highlithing for Gherkin, the language feature files are expressed in.

Example

I include here an example of financial calculation using Behat: the point is calculating Value Added Tax given a price and the percentage of the tax, then summing them to show the gross price to the User. The class under test here is a Price.

Here is a sample feature file:

Feature: VatCalculation
In order to communicate to the client
As an operator
I want to manage value added tax

Scenario: Calculate VAT
Given I have entered 100 into the unit price
And I have entered 20 into the vat percentage
When I make a behavior request
Then The gross price should be 120

An environment:

<?php
// here you could put require_once() statements, or include some bootstrap file

/**
* I define a simple assertion function here. However, it should be in another file.
* You can also import PHPUnit's assertions: everything that throws exceptions when the assertion fails will work.
*/
function assertEquals($expected, $actual)
{
if ($expected != $actual) {
throw new Exception("$expected is not equal to $actual.");
}
}

/**
* Also the class definition should be in its own file: this file will
* be repeatedly executed.
*/
class Price
{
function setUnitPrice($price)
{
$this->unit_price = $price;
}

function setVatPercentage($vat_percentage)
{
$this->vat_percentage = $vat_percentage;
}

function modifiedVat()
{
$this->gross_price = $this->unit_price * (1 + $this->vat_percentage / 100);
}

function getGrossPrice()
{
return $this->gross_price;
}
}

/**
* The fixture for our tests.
*/
$world->price = new Price();

and adequate steps definition:

<?php

$steps->Given('/^I have entered (\d+) into the unit price$/', function($world, $unitPrice) {
$world->price->setUnitPrice($unitPrice);
});
$steps->Given('/^I have entered (\d+) into the vat percentage$/', function($world, $vatPercentage) {
$world->price->setVatPercentage($vatPercentage);
});
$steps->When('/^I make a behavior request$/', function($world) {
$world->price->modifiedVat();
});
$steps->Then('/^The gross price should be (\d+)$/', function($world, $expectedGrossPrice) {
assertEquals($expectedGrossPrice, $world->price->getGrossPrice());
});

However, you shouldn't copy this code, which is here for pure illustration. You can clone my small repository on Github or download an archive from there.

Conclusions

I was initially skeptical about BDD as from the technical point of view it does not add much to TDD. But working on enteprise applications has opened my eyes: I'm the most technical person in my team, but I must collaborate and communicate with the domain expert(s) in order to be aligned with the requirements and what the user wants from my code.

BDD opens up new possibilities like working on specifications together with business people (without having a programmer there to continuosly fix the syntax). I'm sure you have at least one requirements document in your office where specifications are not synchronized anymore with what the code does.

Finally, tests readability cannot be better: the only thing that can rival with steps defined with natural language are Smalltalk's keyword messages.

Published at DZone with permission of Giorgio Sironi, author and DZone MVB.

(Note: Opinions expressed in this article and its replies are the opinions of their respective authors and not those of DZone, Inc.)