Post on 10-May-2015
description
BDD for with
PHPSpec
29th October 2011
Marcello Duarte
Saturday, 29 October 2011
whoami
Marcello Duarte
Head of Training @ Ibuildings UK
Lead developer @ PHPSpec
Twitter @_md
Marcello Duarte
Head of Training @ Ibuildings UK
Lead developer @ PHPSpec
Twitter @_md
Saturday, 29 October 2011
In the beginningthere was...
TDD
credits: http://www.flickr.com/photos/improveit/1574023621
Saturday, 29 October 2011
They saw that it was good
Saturday, 29 October 2011
$thou->shalt->test$thou->shalt->test
credits: http://www.flickr.com/photos/36829113@N05/3392940179/
Saturday, 29 October 2011
BDD
credits: http://www.flickr.com/photos/psd/424257767/
Saturday, 29 October 2011
BDD
A way of teaching TDD
credits: http://www.flickr.com/photos/psd/424257767/
Saturday, 29 October 2011
What to test?
Where to begin?
How do I name my test?
How much to test in one go?
What to call my test??Saturday, 29 October 2011
credits: http://www.flickr.com/photos/psd/424257767/
BDD
Saturday, 29 October 2011
Offers a common language
credits: http://www.flickr.com/photos/psd/424257767/
BDD
Saturday, 29 October 2011
user developer
business
Saturday, 29 October 2011
behavior
user developer
business
Saturday, 29 October 2011
Sapir-Whorf hypothesis
© M
anusc
ripts
and A
rchiv
es,
Yale
Univ
ersi
ty L
ibra
ry.
Saturday, 29 October 2011
Language to express truth
vs...
Saturday, 29 October 2011
Language to discover truth
Saturday, 29 October 2011
Language influence thought
Saturday, 29 October 2011
credits: http://www.flickr.com/photos/psd/424257767/
BDD
Saturday, 29 October 2011
A way to discoverwhat is useful to deliver
credits: http://www.flickr.com/photos/psd/424257767/
BDD
Saturday, 29 October 2011
Saturday, 29 October 2011
BDD Outside in
Gherkin
Behat
PHPSpec
Saturday, 29 October 2011
Gherkin
Saturday, 29 October 2011
Feature: Organizers can open a call for paper As an event organizer I want a way to publish a centralized cfp form So that it’s easier for speakers to submit
Scenario: Creation form with valid attributes Given I am in on "call-for-papers/add" When I fill in the following: | event | PHPLondon Conference| | start_date | 2012-02-06 | | limit_abstract_wc | 500 | | why_you_field | 1 | | offer_hotel | 1 | | offer_travel | 0 | And I press "Create" Then I should see "The cfp was created successfully"
Feature: Organizers can open a call for paper As an event organizer I want a way to publish a centralized cfp form So that it’s easier for speakers to submit
Scenario: Creation form with valid attributes Given I am in on "call-for-papers/add" When I fill in the following: | event | PHPLondon Conference| | start_date | 2012-02-06 | | limit_abstract_wc | 500 | | why_you_field | 1 | | offer_hotel | 1 | | offer_travel | 0 | And I press "Create" Then I should see "The cfp was created successfully"
Feature: Organizers can open a call for paper As an event organizer I want a way to publish a centralized cfp form So that it’s easier for speakers to submit
Scenario: Creation form with valid attributes Given I am in on "call-for-papers/add" When I fill in the following: | event | PHPLondon Conference| | start_date | 2012-02-06 | | limit_abstract_wc | 500 | | why_you_field | 1 | | offer_hotel | 1 | | offer_travel | 0 | And I press "Create" Then I should see "The cfp was created successfully"
Feature: Organizers can open a call for paper As an event organizer I want a way to publish a centralized cfp form So that it’s easier for speakers to submit
Scenario: Creation form with valid attributes Given I am in on "call-for-papers/add" When I fill in the following: | event | PHPLondon Conference| | start_date | 2012-02-06 | | limit_abstract_wc | 500 | | why_you_field | 1 | | offer_hotel | 1 | | offer_travel | 0 | And I press "Create" Then I should see "The cfp was created successfully"
Saturday, 29 October 2011
behat.org
Saturday, 29 October 2011
Saturday, 29 October 2011
words matter
Saturday, 29 October 2011
$this->assertTrue(...
Saturday, 29 October 2011
$this->assertTrue(...
What am I going to test?
Saturday, 29 October 2011
$report = new Report;$this->assertTrue($report instanceof Report);
Saturday, 29 October 2011
$report = new Report;$this->assertTrue($report instanceof Report);//
Saturday, 29 October 2011
$employee->should->...
Saturday, 29 October 2011
$employee->should->...
What is the expected behavior?
Saturday, 29 October 2011
Use Gherkin and Behat for specifying scenarios
Use PHPSpec for specifying classes∫
Saturday, 29 October 2011
expressiveness
Saturday, 29 October 2011
$employee->should->reportTo($manager);
$this->assertTrue($employee->reportsTo($manager));
Saturday, 29 October 2011
phpspec.net
Saturday, 29 October 2011
$ sudo pear channel-discover pear.phpspec.net$ sudo pear install --alldeps phpspec/PHPSpec
Installing
Saturday, 29 October 2011
“Specification, not verification” (Uncle Bob)
$this->assertEquals(0, $result);
becomes$result->should->be(0);
Saturday, 29 October 2011
class CalculatorTest
becomesclass DescribeCalculator
class CalculatorTest
becomesclass DescribeCalculator
Saturday, 29 October 2011
function testAddWithNoArguments()
becomesfunction itReturnsZeroWithNoArguments()
Saturday, 29 October 2011
All together
class DescribeStringCalculator extends \PHPSpec\Context{
function itReturnsZeroWithNoArguments() { $calculator = $this->spec(new StringCalculator); $result = $calculator->add();
$result->should->be(0); }
}
Saturday, 29 October 2011
Hooks
before()after()
beforeAll()afterAll()
Saturday, 29 October 2011
Setting initial state with before hook
class DescribeStringCalculator extends \PHPSpec\Context{ function before() { $this->calculator = $this->spec(new StringCalculator); }
function itReturnsZeroWithNoArguments() { $result = $this->calculator->add(); $result->should->be(0); }}
Saturday, 29 October 2011
class DescribeStringCalculator extends \PHPSpec\Context{ private $calculator;
function before() { $this->calculator = $this->spec(new StringCalculator); }
function itReturnsZeroWithNoArguments() { $this->calculator->add()->should->equal(0); }
function itReturnsTheBareNumber() { $this->calculator->add('42')->should->equal(42); }}
Saturday, 29 October 2011
Formatters
progressdocumentation
html
coming soon:junit
Saturday, 29 October 2011
$ phpspec StringCalculatorSpec.php -c.*.F
Pending: String Calculator returns the bare number # Waiting to clarify the spec # ./spec/StringCalculatorSpec.php:19
Failures: 1) String Calculator returns the sum of space separate string expected 42, got 0 (using be()) # .spec/StringCalculatorSpec.php:28
2) StringCalculator returns the sum of any white space separated string Failure/Error: Just because
Finished in 0.056134 seconds4 examples, 1 failure, 1 pending
Progress Formatter
Saturday, 29 October 2011
HTML Formatter
Saturday, 29 October 2011
Documentation Formatter
Saturday, 29 October 2011
Matchers
Saturday, 29 October 2011
be($match)equal($match)
beEqualTo($match)beAnInstanceOf($match)
beEmpty()beFalse()
beGreaterThan($match)beGreaterThanOrEqualTo($match)
Saturday, 29 October 2011
And more matchers...
Saturday, 29 October 2011
beInteger()beLessThan($match)
beLessThanOrEqualTo($match) beNull()
beString()beTrue()
throwException($match)
Saturday, 29 October 2011
Predicate Matchers
Saturday, 29 October 2011
$cell = $this->spec(new Cell);$cell->should->beAlive();
class Cell{ protected $alive = true;
public function isAlive() { return $this->alive; } ...}
$cell = $this->spec(new Cell);$cell->should->beAlive();
class Cell{ protected $alive = true;
public function isAlive() { return $this->alive; } ...}
Saturday, 29 October 2011
$newNode = $this->spec(new Node);$newNode->shouldNot->haveChildren();
class Node{ protected $children = array();
public function hasChildren() { return count($this->children) > 0; } ...}
$newNode = $this->spec(new Node);$newNode->shouldNot->haveChildren();
class Node{ protected $children = array();
public function hasChildren() { return count($this->children) > 0; } ...}
Saturday, 29 October 2011
Custom Matchers
Saturday, 29 October 2011
\PHPSpec\Matcher\define('reportTo', function($supervisor) { return array ( 'match' => function($supportEngineer) use ($supervisor) { return $supportEngineer->reportsTo($supervisor); }, 'failure_message_for_should' => function($supportEngineer) use ($supervisor) { return "expected " . $supervisor->getName() . " to report to " . $supervisor->getName(); } );});
class DescribeSupportEngineer extends \PHPSpec\Context{ ... function itAddsNewCourses() { $john = new Supervisor("John Smith"); $john->addToTeam($this->supportEngineer); $this->supportEngineer->should->reportTo($john); }}
\PHPSpec\Matcher\define('reportTo', function($supervisor) { return array ( 'match' => function($supportEngineer) use ($supervisor) { return $supportEngineer->reportsTo($supervisor); }, 'failure_message_for_should' => function($supportEngineer) use ($supervisor) { return "expected " . $supervisor->getName() . " to report to " . $supervisor->getName(); } );});
class DescribeSupportEngineer extends \PHPSpec\Context{ ... function itAddsNewCourses() { $john = new Supervisor("John Smith"); $john->addToTeam($this->supportEngineer); $this->supportEngineer->should->reportTo($john); }}
Saturday, 29 October 2011
PHPSpec &
Saturday, 29 October 2011
Tool
framework.zend.com
Saturday, 29 October 2011
$ sudo pear channel-discover pear.zfcampus.org$ sudo pear install zfcampus/zf$ zf create config$ vi ~/.zf.ini
Installing
Saturday, 29 October 2011
.zf.ini
php.include_path = ".:/usr/share/pear"
basicloader.classes.1 = "Akrabat_Tool_DatabaseSchemaProvider"basicloader.classes.2 = "PHPSpec_Context_Zend_Tool_Provider_Phpspec"basicloader.classes.3 = "PHPSpec_Context_Zend_Tool_Provider_ModelSpec"basicloader.classes.4 = "PHPSpec_Context_Zend_Tool_Provider_ViewSpec"basicloader.classes.5 = "PHPSpec_Context_Zend_Tool_Provider_ControllerSpec"basicloader.classes.6 = "PHPSpec_Context_Zend_Tool_Provider_ActionSpec"basicloader.classes.7 = "PHPSpec_Context_Zend_Tool_Provider_Behat"
php.include_path = ".:/usr/share/pear"
basicloader.classes.1 = "Akrabat_Tool_DatabaseSchemaProvider"basicloader.classes.2 = "PHPSpec_Context_Zend_Tool_Provider_Phpspec"basicloader.classes.3 = "PHPSpec_Context_Zend_Tool_Provider_ModelSpec"basicloader.classes.4 = "PHPSpec_Context_Zend_Tool_Provider_ViewSpec"basicloader.classes.5 = "PHPSpec_Context_Zend_Tool_Provider_ControllerSpec"basicloader.classes.6 = "PHPSpec_Context_Zend_Tool_Provider_ActionSpec"basicloader.classes.7 = "PHPSpec_Context_Zend_Tool_Provider_Behat"
Saturday, 29 October 2011
$ zf create project callconfCreating project at /var/www/callconfNote: This command created a web project, for more information setting up your VHOST, please see docs/README
create a project
Saturday, 29 October 2011
$ cd callconf$ zf generate phpspec create spec create spec/SpecHelper.php create spec/.phpspec create spec/models create spec/views create spec/controllers
initialize PHPSpec
Saturday, 29 October 2011
$ zf generate behat+d features - place your *.feature files here+d features/bootstrap - place bootstrap scripts and static files here+f features/bootstrap/FeatureContext.php - place your feature related code here
initialize Behat
Saturday, 29 October 2011
Saturday, 29 October 2011
Views
Saturday, 29 October 2011
why specify the view?
Saturday, 29 October 2011
Controller and model to the point
Ensure we are focused on what matters
Sustainable pace
Saturday, 29 October 2011
create a view spec
Saturday, 29 October 2011
$ zf create view-spec add CallForPapers
create a view spec
Saturday, 29 October 2011
$ zf create view-spec add CallForPapers Creating a view script in location /var/www/callconf/application/views/scripts/call-for-papers/add.phtmlCreating a spec at /var/www/callconf/spec/views/call-for-papers/AddSpec.php
create a view spec
Saturday, 29 October 2011
Saturday, 29 October 2011
Spec created by default<?php
namespace CallForPapers;
require_once __DIR__ . '/../../SpecHelper.php';
use \PHPSpec\Context\Zend\View as ViewContext;
class DescribeAdd extends ViewContext{ function itRendersTheDefaultContent() { $this->render(); $this->rendered->should->contain('CallForPapers'); $this->rendered->should->contain('add'); }}
<?php
namespace CallForPapers;
require_once __DIR__ . '/../../SpecHelper.php';
use \PHPSpec\Context\Zend\View as ViewContext;
class DescribeAdd extends ViewContext{ function itRendersTheDefaultContent() { $this->render(); $this->rendered->should->contain('CallForPapers'); $this->rendered->should->contain('add'); }}
<?php
namespace CallForPapers;
require_once __DIR__ . '/../../SpecHelper.php';
use \PHPSpec\Context\Zend\View as ViewContext;
class DescribeAdd extends ViewContext{ function itRendersTheDefaultContent() { $this->render(); $this->rendered->should->contain('CallForPapers'); $this->rendered->should->contain('add'); }}
Saturday, 29 October 2011
what behaviors can we describe in the view spec?
Saturday, 29 October 2011
Variables we need assigned
What content was rendered(We can use selectors)
Saturday, 29 October 2011
Assigning Variables
function itRendersTheTalkAbstract(){ $marcello = $this->mock('Speaker', array('isVegetarian' => true)); $this->assign('speaker', $marcello); $this->render(); $this->rendered->should->contain('diet restrictions: vegetarian');}
Saturday, 29 October 2011
Controllers
Saturday, 29 October 2011
create a controller spec
Saturday, 29 October 2011
$ zf create controller-spec CallForPapers add,create
create a controller spec
Saturday, 29 October 2011
$ zf create controller-spec CallForPapers add,createCreating a controller at /private/var/www/callconf/application/controllers/CallForPapersController.phpCreating an add action method in controller CallForPapersCreating an create action method in controller CallForPapersCreating a spec at /private/var/www/callconf/spec/controllers/CallForPapersSpec.php
create a controller spec
Saturday, 29 October 2011
Saturday, 29 October 2011
Spec created by default
<?php
require_once __DIR__ . '/../SpecHelper.php';
class DescribeCallForPapers extends \PHPSpec\Context\Zend\Controller{ function itShouldBeSuccessfulToGetAdd() { $this->get('call-for-papers/add'); $this->response->should->beSuccess(); }
}
<?php
require_once __DIR__ . '/../SpecHelper.php';
class DescribeCallForPapers extends \PHPSpec\Context\Zend\Controller{ function itShouldBeSuccessfulToGetAdd() { $this->get('call-for-papers/add'); $this->response->should->beSuccess(); }
}
Saturday, 29 October 2011
what behaviors do we want todescribe in the controller spec?
Saturday, 29 October 2011
How do we want to route to its actions
What view variables we need assigned
What view we want rendered
Saturday, 29 October 2011
Routing and assigning
function itShouldRouteToTheAddAction() { $this->routeFor(array( 'controller' => 'call-for-papers', 'action' => 'add' ))->should->be('/call-for-papers/add'); } function itAssignsAddSubmissionFormVariable() { $this->get('/call-for-papers/add'); $this->assigns('addSubmissionForm')->should->beAnInstanceOf( '\Application_Form_AddSubmissionForm' ); }
function itShouldRouteToTheAddAction() { $this->routeFor(array( 'controller' => 'call-for-papers', 'action' => 'add' ))->should->be('/call-for-papers/add'); } function itAssignsAddSubmissionFormVariable() { $this->get('/call-for-papers/add'); $this->assigns('addSubmissionForm')->should->beAnInstanceOf( '\Application_Form_AddSubmissionForm' ); }
Saturday, 29 October 2011
Models
Saturday, 29 October 2011
create a model spec
Saturday, 29 October 2011
$ zf create model-spec Speaker name:string,email:string
create a model spec
Saturday, 29 October 2011
$ zf create model-spec Speaker name:string,email:stringCreating a model at /private/var/www/callconf/application/models/Speaker.phpCreating a db table at /private/var/www/callconf/application/models/DbTable/Speakers.phpCreating a mapper at /private/var/www/callconf/application/models/SpeakerMapper.phpCreating a spec at /private/var/www/callconf/spec/models/SpeakerSpec.phpCreating migration scripts at /private/var/www/callconf/db/migrate/001-CreateSpeakersTable.phpUpdating project profile '/private/var/www/callconf/.zfproject.xml'
create a model spec
Saturday, 29 October 2011
Saturday, 29 October 2011
Spec created by default<?php
require_once __DIR__ . '/../SpecHelper.php';
use Application_Model_Speaker as Speaker;
class DescribeSpeaker extends \PHPSpec\Context{ function before() { $this->validAttributes = array( 'name' => 'value for name', 'email' => 'value for email', ); } function itShouldCreateANewInstanceGivenValidAttributes() { $this->speaker = $this->spec(Speaker::create($this->validAttributes)); $this->speaker->should->beValid(); }}Saturday, 29 October 2011
what behaviors can wedescribe in the model spec?
Saturday, 29 October 2011
Business logic
Validation
Spying results from data source operations
Saturday, 29 October 2011
Business Logic
class DescribeSpeaker extends \PHPSpec\Context{ function before() { $this->validAttributes = array( 'name' => 'Marcello Duarte', 'email' => 'marcello@ibuildings.com', 'diet_restriction' => 'vegetarian', ); $this->speaker = $this->spec(Speaker::create($this->validAttributes)); } function itGetsExtraRatingPointsForTalkIfVegetarian() { $this->speaker->should->haveExtraPoints(); }}
class DescribeSpeaker extends \PHPSpec\Context{ function before() { $this->validAttributes = array( 'name' => 'Marcello Duarte', 'email' => 'marcello@ibuildings.com', 'diet_restriction' => 'vegetarian', ); $this->speaker = $this->spec(Speaker::create($this->validAttributes)); } function itGetsExtraRatingPointsForTalkIfVegetarian() { $this->speaker->should->haveExtraPoints(); }}
Saturday, 29 October 2011
Business Logic
class Speaker{ //... other methods
function hasExtraPoints() { return stripos($this->getDietRestrictions(), 'vegetarian') !== false; }}
class Speaker{ //... other methods
function hasExtraPoints() { return stripos($this->getDietRestrictions(), 'vegetarian') !== false; }}
Saturday, 29 October 2011
Real database hits?
Saturday, 29 October 2011
Avoid
Sometimes, for confidence
When testing data access objects
Saturday, 29 October 2011
Dependency chains
Saturday, 29 October 2011
Dependencies can be hard to manage
class DescribeEvent extends \PHPSpec\Context{ function itDoesSomethingWhenYouHaveSpeakerAllocated() { $event = new Event( new Organizer('John Smith', new Organization('Ibuildings') ) ); $event->addSpeaker(new Speaker('Rowan'), new Slot(’10:30’), new Room('A')); $event->addSpeaker(new Speaker('Ben'), new Slot(’10:30’), new Room('B')); // specify expected behavior }}
Saturday, 29 October 2011
Usually dependencies are replaced with doubles when writing specs
We can use a framework like Mockery
But if you really need the real thing
Saturday, 29 October 2011
Object Mother
Saturday, 29 October 2011
Dependencies can be hard to manage
class DescribeEvent extends \PHPSpec\Context{ function itDoesSomethingWhenYouHaveSpeakerAllocated() { $exampleEvent = ExampleEvent::newWithSimultaneousSpeakers();
// specify expected event behavior }}
Saturday, 29 October 2011
Code duplication
Too many methods
Saturday, 29 October 2011
Test Data Builder
Saturday, 29 October 2011
Is created with save “empty” objects
Has a fluent interface
Has a build method
Saturday, 29 October 2011
Dependencies can be hard to manage
class DescribeEvent extends \PHPSpec\Context{ function itDoesSomethingWhenYouHaveSpeakerAllocated() { $eventBuilder = new EventBuilder(); $organizerBuilder = new OrganizerBuilder();
$event = $eventBuilder->withOrganizer( $organizerBuilder->withOrganization()->build() )->withConflictingSpeakers() ->build();
// specify expected event behavior }}
Saturday, 29 October 2011
phactory.org
Saturday, 29 October 2011
installing
Saturday, 29 October 2011
$ sudo pear channel-discover pearhub.org
installing
Saturday, 29 October 2011
$ sudo pear channel-discover pearhub.org$ sudo pear install pearhub/Phactory
installing
Saturday, 29 October 2011
Needs a Pdo connection
Get from default adapter
Saturday, 29 October 2011
protected function _initPhactory() { Phactory::setConnection( Zend_Db_Table_Abstract::getDefaultAdapter()); return Phactory::getConnection(); }
Create a connection
Saturday, 29 October 2011
// spec/factories.php
Phactory::define('speaker', array( 'name' => 'John Smith', 'email' => 'john@smith.com'));
Define table blueprints
Saturday, 29 October 2011
// in one of my specs
$ben = Phactory::create('speaker', array('name' => 'Rowan'));$rowan = Phactory::create('speaker', array('name' => 'Ben'));
// Phactory_Row objectsecho $ben->name // prints Ben
Create objects
Saturday, 29 October 2011
Questions?
223
Saturday, 29 October 2011
224
Thank you!
http://joind.in/4318
http://slidesha.re/tcGM93
Marcello Duarte
@_md
is hiring. Come talk to me.
Saturday, 29 October 2011