Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Designing Klingon Warships Using Behaviour Driven Development

DZone's Guide to

Designing Klingon Warships Using Behaviour Driven Development

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

A short time ago, I wrote an article for Devzone entitled An Introduction to the Art of Unit Testing in PHP which explained why testing, and especially automated testing, is an important aspect of developing in any programming language. I also offered a brief example of applying a design methodology called Test-Driven Development, which demonstrated that writing tests before code led to improved simpler design free of bugs.

In this article, I introduce a TDD related practice called Behaviour-Driven Development which has been gathering attention for over a year and gaining converts (like me!).

Test-Driven Development

Back in the old days of eXtreme Programming (XP), nobody really considered TDD. It was all about testing everything that could possibly go wrong (going to extremes in true XP style), to prevent it from inevitably going wrong. If you're just starting out with Unit Testing you've probably noticed how untested code seems to collect bugs at an incredible rate.

Then people started to hear about using Tests to drive the design of applications and how this miraculous discovery led to simpler more robust source code of a higher quality and with a superior design. It wasn't long before Test-Driven Development became the darling of the XP and Agile movements, with dozens of books and hundreds of articles extolling its virtues. Frameworks originally designed for Testing were quickly adopted for use in TDD. PHP has since seen the rise of SimpleTest and PHPUnit, and even PEAR's PHPT runner.

But the road to TDD expertise was paved with potholes and detours, bad signage and dead ends. Today, in PHP at least, TDD remains a fuzzy idea which many developers have not adopted. Even when they do there is a litany of poor practices that are relatively common. The problem with TDD is that it's historically associated with Testing. But at the same time, it's not actually a testing methodology. This confusing point has sent more than one developer off into the wilds.

Bridging the gap between Testing and Test-Driven Development is even a predictable process. It goes something like this:

  1. A developer gets curious and starts writing Unit Tests for their code.
  2. As the number of tests swell, she enjoys a sudden burst of confidence knowing these tests are watching her back for any bugs thinking of slinking into an application.
  3. Enthused by her confidence, she may decide after reading some TDD articles to give the practice a trial run and see how it goes.
  4. In a moment of inspiration, she realises that each test in TDD looks like a verified use case, or example, documenting how to use the code being written. Tests as documentation becomes a real consideration.
  5. Since tests document examples/use cases, it stands to reason they document the public API/interface of classes. This revelation finally starts to illuminate how TDD really works. Designing and testing public interfaces becomes the focal point for writing tests when practicing TDD. Testing of state (private and protected resources) becomes increasingly rare. API design is suddenly very important.
  6. She finally becomes aware that designing a public API through tests equates to examining and specifying the behaviour of objects. After this Eureka moment, her expertise is undeniable. Explanations of TDD she gives now are made in terms of "behaviour" - the magic B word.
  7. Behaviour has two elements - public interface, and object interactions. Interest in Mock Objects may increase at this point if supported by the unit testing framework in use.

Here's a huge problem though. At step 4, far too many developers either a) consider TDD gibberish, or b) don't quite hit the revelation that tests are not actually tests, but examples of desired behaviour. It's a process that has been repeated countless times. Often, if they're lucky, someone at step 4 will meet someone else at step 5+ and receive a healthy boost from pair programming or some other form of cooperative development.

Behaviour-Driven Development

What happens at step 4 to make developers fail in TDD - or at least cause them not to progress much further except through gradual self-discovery? Many fingers recently have pointed to the T in TDD. TDD was developed as a design process, but because of its origins it became a design process dressed up in the terminology of a testing process. Simply put, it confused the hell out of everyone - even me; I spent a while struggling with TDD! I know lots of people who still insist TDD is about testing - doesn't matter how many TDD experts say otherwise, once it's ingrained it's a difficult belief to exorcise.

Behaviour-Driven Development (BDD) was formulated by Dan North (ThoughtWorks, home of Martin Fowler) to progress TDD to the next level. As a process freed of it's testing misconceptions with feeders reaching into Domain-Driven Design and Acceptance-Testing Driven Development (two other important methodologies TDD lives alongside in XP). The funny thing about DDD and ATDD is that they are more focused on non-developers. Afterall, the client really doesn't care about Unit Tests, but hand them plain text specifications, FIT data, user stories or UI mock ups and they'll be happy as mud (hopefully!). Clients very much prefer material they can edit themselves.

So BDD focuses more on three levels. First - it focuses on specifying behaviour. It's what you will do once you hit step 6 on the TDD ladder, so might as well just start doing it now and cheat by adopting a test-agnostic language! Secondly, it declares documentation an essential consideration in communication (whether other developers or stakeholders/clients) - so out with tests and funky PHP syntax that's not very readable, and in with grammatically correct, clear, plain text (or a close facsimile in the source code world - a domain specific language (DSL)). Thirdly, by avoiding any link to Testing the language of BDD puts you in a better frame of reference. These points lead inevitably to improved design - the golden goal of TDD at the end of the day - and much easier translation from client requirements to nuts and bolts source code.

So in essence BDD has three focuses: behaviour, documentation and design.

Bringing Back The Business Value Focus

TDD is usually looked at in terms of code and programming. You have the class, its methods, and they need to be designed by writing tests. This isn't invalid in one sense - a lot of crazy development goes on at the class level. But TDD isn't an isolated practice. There's also Domain-Driven Design (DDD) and something I wrote about some months ago called Acceptance Testing (ATDD).

Both of these two disciplines are client facing. Clients may not only discuss acceptance criteria and business logic, but they may literally define and write the user stories and tests themselves (maybe with a little help and developer involvement). Consider the popularity of FIT. If you ever find a business analyst with the FIT bug it's a beautiful thing. Of course, most of these "tests" are really specifications. What the system should do given A, when I do B, and then C, i.e. User Stories broken into a sequence of repeatable events and constraints. Since these User Stories drive your eventual goals, they also directly drive what you do in the TDD and DDD processes. If a client talks about Accounts and Customers, it's not going to be hard to guess you'll be TDD'ing a few Models down the line and you need to mediate between client requirements for business rules and PHP code.

The problem is that developers and clients need to speak the same language. Clients likely don't write PHP, and you likely don't write tests in plain English, so something must appear to bind them together. DDD calls this the ubiquitous language.

"A domain model can be the core of a common language for a software project. The model is a set of concepts built up in the heads of people on the project, with terms and relationships that reflect domain insight. These terms and interrelationships provide the semantics of a language that is tailored to the domain while being precise enough for technical development. This is a crucial cord that weaves the model into development activity and binds it with the code."

- Evans 2003

TDD doesn't have that (I can always mention FIT once more I suppose). BDD on the other hand does. If you ever wondering why BDD is so obsessed with concepts like Domain Specific Languages and fluent interfaces patterned on grammatically correct English, then there's your explanation. It's the goal of using a form of specification which is a) client readable, b) client editable, and c) capable of automated execution.

TDD falls at this hurdle. Unit Tests are written in the native programming language without any concern for English grammer.

From a PHP perspective it's very hard to demonstrate this (there's no feature-complete BDD framework available just yet - though there is one in progress) but in Java or Ruby it's captured on two levels. Firstly, the level of low lying source code where TDD is generally the most visible (we'll visit there later), and secondly at the DDD/ATDT level where client User Stories are translated into Acceptance Tests written as close as possible to plain English while still being executable.

Consider an example of some business logic:

    Story: I have a savings and current account and I want
to transfer money from savings to current.

Scenario: savings account has enough money
Given my savings account balance is 100
And my current account balance is 50
When I transfer 20 from savings to current
Then my savings account balance should be 80
And my current account balance should be 70

It's in plain English and, odd though it looks, it could easily be executable (a little parsing and a suitable runner). If you've ever used YAML something is likely clicking right about now. The point here is that this format is fairly simple. You have a User Story, and set of Scenarios to demonstrate how the User Story can be specified in terms of valued behaviour. The format is plain text (zero code) so it's easy to edit by a non-developer. At the same time, it's highly structured so it could feasibly be parsed to code and executed as an Acceptance Test.

Moving From Testing to Specifying Behaviour

We'll leave Scenarios for another article one day, for the moment let's drop back to the level of source code and talk about behaviour. As I noted previously, if you hit Step 6 on the TDD ladder there's a sudden revelation that TDD is always best applied by describing the behaviour of an object or cluster of objects. The goal of TDD is not to create an expansive regression testing suite, it's to write tests in such a style that behaviour is described, addresses the current problem piece by piece, and leads to a simpler more obvious solution with a better design.

That's not to say testing is unimportant! But it's a beneficial outcome of applying a suitable TDD/BDD process, while not being to end goal in and of itself.

Now, behaviour can be described in the form: Given some context, when I do this, then it should do something interesting.

Maybe the earlier Scenario example is familiar here; we're using the words "Given", "When", "Then" and "Should" once more. These words are actually very compelling when analysing and describing behaviour. They offer a very structured way of thinking which fits in with TDD or BDD equally well. In fact, this mode of thinking is hugely important since it escapes many developers trying to learn TDD.

The problem met prior to reaching Step 6 on our TDD ladder however, is that TDD is based on a non-TDD structure - Unit Testing has no structure to frame behaviour and leaves any structured best practice to be discovered on your own (if lucky). It's to resolve this problem that dedicated Behaviour-Driven Development frameworks have been written for everything from Java to Smalltalk. Such frameworks offer a dedicated structure and means for specifying behaviour from the very start.

Unfortunately, this is often viewed as a duplicate Unit Testing framework because the resulting specs don't seem all that impressive until you catch the behaviour bug and recognise the signs of a simple well structured example. More than one person has considered BDD and admitted to not using explanatory method names to explain a test, and that's just one habit BDD pushes right from the start.

The important thing is that what is optional in a UT framework, is enforced in a BDD framework. If you won't reach Step 6 yourself, you'll be thrown at it from your first spec. This considerably reduces the learning curve present in TDD, gets users thinking in the right way almost immediately, and does it all without using the "test" word.

Let's take a quick example at the code level.

We've gotten a request from some guy dressed up as a Klingon to write a control application for a Klingon Bird of Prey. He's been a bit vague on details, but he did jot down some basic ideas, including this one in English:

"When my ship fires its disruptors it will reduce available power."

It's kind of vague, so he was pushed a little further to define a more specific Scenario we can actually specify.

"Given ship power of 100 mega joules, when my ship fires its disruptors at a Romulan dog, then power will be 99 mega joules."

Based on the above, we can whip out a BDD framework (using PHPSpec here which I wrote; disclaimer!) and set to encoding this scant specification into an executable example (spec) showing how to get an object to exhibit this behaviour.

class DescribeKlingonBirdOfPrey extends PHPSpec_Context {

public function itShouldHaveReducedPowerAfterItsDisruptorsAreFired() {
$ship = new Ship_Klingon_BirdOfPrey;
$ship->power = 100;
$ship->fire();
$this->spec($ship->power)->should->be(99);
}

}


The class can be saved to a file called "KlingonBirdOfPreySpec.php" or a file with the name as the class. Either are allowable. Context here is not really definable, so we'll just go with the class name for now. Later on you might explore whether behaviour changes if a Ship is newly commissioned, in disrepair, or heading for a scrap heap. Context can be oddly important if it globally impacts all behaviours in some way.

A few days later, the weird guy (very genuine looking) has returned in a foul mood and beats his chest before roaring:

"Given ship power of more then 1 mega joule, when power reaches 1 mega joule, then my ship will self-destruct automatically (so my sub-commander can die with honour! Qapla'!)."

He leaves in a hurry, but anyway...

Add another spec for this new behaviour. If you're inventive you might look for any complementary or even opposing behaviour, e.g. can we assume our Klingon friend did not intend the ship to self destruct if power was already 1 mega joule? How do we define minimum levels anyway? Some things to discuss if he ever comes knocking tomorrow...

class DescribeKlingonBirdOfPrey extends PHPSpec_Context {

public function itShouldHaveReducedPowerAfterItsDisruptorsAreFired() {
$ship = new Ship_Klingon_BirdOfPrey;
$ship->power = 100;
$ship->fire();
$this->spec($ship->power)->should->be(99);
}

public function itShouldSelfDestructWhenPowerHitsMinimumLevels() {
$ship = new Ship_Klingon_BirdOfPrey;
$ship->power = 2;
$ship->fire();
$this->spec($ship)->should->haveSelfDestructed(); // predicates to hasSelfDestructed()
}

public function itShouldNotSelfDestructIfPowerOriginallyStartsAtMinimumLevels() {
$ship = new Ship_Klingon_BirdOfPrey;
$ship->power = 1;
$ship->fire();
$this->spec($ship)->shouldNot->haveSelfDestructed(); // predicates to hasSelfDestructed()
}

}


An implementation:

class Ship_Klingon_BirdOfPrey {

public $power = 0;

private $_selfDestructed = false;

public function fire() {
$this->power--;
if ($this->power == 1) {
$this->_selfDestruct();
}
}

public function hasSelfDestructed() {
return $this->_selfDestructed;
}

private function _selfDestruct() {
$this->_selfDestructed = true;
}

}


You probably get the point by now, whenever a valuable piece of behaviour is identified you write an example of it. With PHPSpec, as with any BDD framework, the class confines are highly structured. Class names must start with "Describe" and spec method names must start with "itShould". Expectations are declared using the PHPSpec domain specific language. To explain all this, you can run the specs using the PHPSpec command line runner using the "--specdoc" or shorter "-s" option:

    $ phpspec DescribeKlingonBirdOfPrey --specdoc

...

Finished in 0.004152936935 seconds

klingon bird of prey
-should have reduced power after its disruptors are fired
-should self destruct when power hits minimum levels
-should not self destruct if power originally starts at minimum levels


3 examples, 0 failures

The specdoc output is simply a list of all method names split into a viable sentence for each Context. Let's examine all this from the perspective of BDD's core focuses on Behaviour, Documentation and Design.

Behaviour; The specs written are basically examples of behaviour we consider valuable and which we want our object (the ship) to exhibit. In many ways they are very similar to unit tests with a few key best practices encouraged - the main being one expectation per example. The problem with many expectations in any one example, is that it can mean your behaviour is not fine grained enough. This can be problematic if the aggregated behaviours are mutually exclusive, and by not identifying them separately, you can't identify problems specific to one and not both.

Documentation; Specs are examples, they are titled by a method name which describes the behaviour the example is demonstrating, all specs can be output in plain text for reading/review by anyone outside the developer group. As such, method names can be edited now and again to keep them specific and focused. The examples are very focused on one specific behaviour, with only one specification defined per example. Expectation leverage a domain specific language which closely tracks grammatically correct English.

Design; Now that we're no longer testing, we're free to implement behaviour. This is not very different to TDD, the different is really in how we got here by specifying the valuable behaviour, documenting it, and only then considering its implementation. As with TDD, each incremental step simplifies the problem we're seeking to solve as we face one piece of it at a time. We should continually refactor to keep the code clean of the usual code smells. In focusing on behaviour deducting additional needed behaviours is simplified. Design should result in simpler more elegant robust code which is easier to maintain, and generally resulting in fewer bugs.

BDD with PHPUnit/SimpleTest?

An important aspect of whether you can borrow the BDD encouraged structure and practices and apply them with a Unit Testing framework is dependent on a few factors.

The main one, is how well the framework supports Mock Objects. BDD is an "outside in" process, since behaviours are exhibited on the outer layers of any system, and implementation starts from the external surface area (e.g. Klingon Bird of Prey) and drives ever deeper (e.g. Fire Control by a Weapons System, Disruptor Model and specifications).

Mock Objects are perfect for this approach - once external behaviours are identified, and implementation progresses, Mock Objects can mock out deeper set objects you want to add, but which don't yet exist. Since a Mock Object describes object interactions - they ultimately build up the next layer's public API and expected return values, opening the door to a new set of Specifications on that basis to implement them (or not - some new objects may actually creep up to the surface, e.g. in a fluent interface). It's important to note here that Stubs are not capable of the same feat - Stubs are incapable of containing expectations and have no knowledge of object interactions (i.e. how one object calls and utilises another, the order of calling, the number of calls, valid argument lists, etc.).

PHPUnit at the present time has a fairly weak Mock Object implementation. It's easy to criticise what is afterall a reasonably new feature, and there is a push for an independent Mock Object framework in PHP to close the gap. SimpleTest on the other hand has had excellent Mock Object support for many years, while PHPT has no Mock Object support full stop (another justification for an independent Mock Object framework!).

A second factor is enabling a BDD style spec structure. Neither unit testing framework is presently capable of a plain English styled domain specific language for specifications since they follow the BDD disputed convention of Test "assertions" written purely for PHP syntax, though a recent blog post by Raphael Stolt shows some resemblance is possible. PHPUnit also allows method names to be abstracted away from including the word "test", though it still requires such methods to be marked with an "@test" annotation. Still, if you want to apply BDD on an existing PHPUnit project, then the possibility bears more examination.

The third factor is the implementation of User Story and Scenario support which neither unit test framework has or is planning. Notably, neither does PHPSpec right now either! One day soon... This is quite an important aspect of BDD's goals in crossing the gap between developers and clients which TDD does not integrate explicitly. A code level spec framework is just one side of the BDD equation. Our Bird Of Prey example, since it delves into business logic of a sort, may be better suited to the plain text story format a Klingon could directly edit themselves.

Conclusion

Hopefully this introduction to Behaviour-Driven Development has been both insightful and useful. If you have any questions, please post a comment below and I'll even limit my response so it's slightly shorter than this article!

Copyright (c) 2007 Padraic Brady

This tutorial first appeard on Zend's DevZone and is used by permission.

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

Topics:

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}