Support for BDD and Stories in PHPUnit 3.3
PHPUnit_Extensions_Story_TestCase is a new extension for PHPUnit that has been contributed by Xait, a company that I visited last fall. It adds a story framework with a Domain-Specific Language (DSL) for Behaviour-Driven Development (BDD).
Start by writing a Story and a @scenario:
<?php
require_once 'PHPUnit/Extensions/Story/TestCase.php';
require_once 'BowlingGame.php';
class BowlingGameSpec extends PHPUnit_Extensions_Story_TestCase
{
/**
* @scenario
*/
public function scoreForOneSpareIs16()
{
$this->given('New game')
->when('Player rolls', 5)
->and('Player rolls', 5)
->and('Player rolls', 3)
->then('Score should be', 16);
}
// ...
}
?>
Each given(), when() and then() is a Step. The and()s are each the same kind as the previous Step. Steps get defined like this:
<?php
require_once 'PHPUnit/Extensions/Story/TestCase.php';
require_once 'BowlingGame.php';
class BowlingGameSpec extends PHPUnit_Extensions_Story_TestCase
{
// ...
public function runGiven(&$world, $action, $arguments)
{
switch($action) {
case 'New game': {
$world['game'] = new BowlingGame;
$world['rolls'] = 0;
}
break;
default: {
return $this->notImplemented($action);
}
}
}
public function runWhen(&$world, $action, $arguments)
{
switch($action) {
case 'Player rolls': {
$world['game']->roll($arguments[0]);
$world['rolls']++;
}
break;
default: {
return $this->notImplemented($action);
}
}
}
public function runThen(&$world, $action, $arguments)
{
switch($action) {
case 'Score should be': {
for ($i = $world['rolls']; $i < 20; $i++) {
$world['game']->roll(0);
}
$this->assertEquals($arguments[0], $world['game']->score());
}
break;
default: {
return $this->notImplemented($action);
}
}
}
}
?>
We can now ask PHPUnit to "tell our stories":
sb@vmware ~ % phpunit --story BowlingGameSpec
PHPUnit @package_version@ by Sebastian Bergmann.
BowlingGameSpec
- Score for gutter game should be 0 [successful]
Given New game
Then Score should be 0
- Score for all ones is 20 [successful]
Given New game
When Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
and Player rolls 1
Then Score should be 20
- Score for one spare is 16 [successful]
Given New game
When Player rolls 5
and Player rolls 5
and Player rolls 3
Then Score should be 16
- Score for one strike is 24 [successful]
Given New game
When Player rolls 10
and Player rolls 3
and Player rolls 4
Then Score should be 24
- Score for perfect game is 300 [successful]
Given New game
When Player rolls 10
and Player rolls 10
and Player rolls 10
and Player rolls 10
and Player rolls 10
and Player rolls 10
and Player rolls 10
and Player rolls 10
and Player rolls 10
and Player rolls 10
and Player rolls 10
and Player rolls 10
Then Score should be 300
Scenarios: 5, Failed: 0, Skipped: 0, Incomplete: 0.
17/01/2008 at 20:30 Permalink
Reply
19/06/2008 at 17:12 Permalink
Reply
19/08/2008 at 16:41 Permalink
_whenAuthorizedUser
_thenGiveAccess
or use
/**
* @when
* @given
*/
In this way single methods don't exceed 20 lines. Additionally, in code documentation would be simplified.
I appreciate the work on the project greatly.
Reply
04/09/2009 at 02:02 Permalink
runThen()...
for ($i = $world['rolls']; $i < 20; $i++) {
$world['game']->roll(0);
}
is a very bad smell. You're altering your code under test in the assertion which defeats the purpose. It also means that the first spec (new game score == 0) will always pass.
Better to get rid of that for loop altogether and see if everything still passes: it should be a characteristic of BowlingGame that a newly instantiated instance has a score of 0, not a characteristic of the test.
Reply
16/02/2010 at 15:43 Permalink
I would like the story result to be appeared on my cruiseControl. Currently, the --log-xml doesn't include story result.
Reply
17/02/2010 at 16:39 Permalink
By the way, version 3.4 doesn't seem to have metrics generation for phpUnderControl.
Reply
19/07/2010 at 00:35 Permalink
I'm working on some similar PHPUnit extensions that use a Cucumber/JBehave style approach and run test suites by parsing text files with DSL scenarios in them by mapping the various scenario steps to user specified step classes.
I have a rough working prototype running but I'm ironing out a few issues, consolidating the API and adding some polish before posting my alpha source code to my google project page.
I have a few images and some information up here http://code.google.com/p/phpconform/downloads/list
I'd love to hear any thoughts you have on this if you have the time Sebastian.
Reply