BDD For Zend Framework With PHPSpec

6,106 views

Published on

Talk delivered at PHP Barcelona Conference 2011. Using PHPSpec and Zend Tool together to demonstrate BDD in a PHP MVC context.

Published in: Technology, Design

BDD For Zend Framework With PHPSpec

  1. 1. BDD for with PHPSpec 29th October 2011 Marcello DuarteSaturday, 29 October 2011
  2. 2. whoami Marcello Duarte Head of Training @ Ibuildings UK Lead developer @ PHPSpec Twitter @_mdSaturday, 29 October 2011
  3. 3. In the beginning there was... TDD credits: http://www.flickr.com/photos/improveit/1574023621Saturday, 29 October 2011
  4. 4. They saw that it was goodSaturday, 29 October 2011
  5. 5. $thou->shalt->test credits: http://www.flickr.com/photos/36829113@N05/3392940179/Saturday, 29 October 2011
  6. 6. BDD credits: http://www.flickr.com/photos/psd/424257767/Saturday, 29 October 2011
  7. 7. BDD A way of teaching TDD credits: http://www.flickr.com/photos/psd/424257767/Saturday, 29 October 2011
  8. 8. What to test? H Where to begin? ow m uchSaturday, 29 October 2011 to test i ? What to call my test? How do I name my test? n one go?
  9. 9. BDD credits: http://www.flickr.com/photos/psd/424257767/Saturday, 29 October 2011
  10. 10. BDD Offers a common language credits: http://www.flickr.com/photos/psd/424257767/Saturday, 29 October 2011
  11. 11. business user developerSaturday, 29 October 2011
  12. 12. business user developer behaviorSaturday, 29 October 2011
  13. 13. Saturday, 29 October 2011 © Manuscripts and Archives, Yale University Library. Sapir-Whorf hypothesis
  14. 14. Language to express truth vs...Saturday, 29 October 2011
  15. 15. Language to discover truthSaturday, 29 October 2011
  16. 16. Language influence thoughtSaturday, 29 October 2011
  17. 17. BDD credits: http://www.flickr.com/photos/psd/424257767/Saturday, 29 October 2011
  18. 18. BDD A way to discover what is useful to deliver credits: http://www.flickr.com/photos/psd/424257767/Saturday, 29 October 2011
  19. 19. Saturday, 29 October 2011
  20. 20. BDD Outside in Gherkin Behat PHPSpecSaturday, 29 October 2011
  21. 21. GherkinSaturday, 29 October 2011
  22. 22. 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
  23. 23. behat.orgSaturday, 29 October 2011
  24. 24. Saturday, 29 October 2011
  25. 25. words matterSaturday, 29 October 2011
  26. 26. $this->assertTrue(...Saturday, 29 October 2011
  27. 27. $this->assertTrue(... What am I going to test?Saturday, 29 October 2011
  28. 28. $report = new Report; $this->assertTrue($report instanceof Report);Saturday, 29 October 2011
  29. 29. / / $report = new Report; $this->assertTrue($report instanceof Report);Saturday, 29 October 2011
  30. 30. $employee->should->...Saturday, 29 October 2011
  31. 31. $employee->should->... What is the expected behavior?Saturday, 29 October 2011
  32. 32. Use Gherkin and Behat for specifying scenarios ∫ Use PHPSpec for specifying classesSaturday, 29 October 2011
  33. 33. expressivenessSaturday, 29 October 2011
  34. 34. $this->assertTrue($employee->reportsTo($manager)); $employee->should->reportTo($manager);Saturday, 29 October 2011
  35. 35. phpspec.netSaturday, 29 October 2011
  36. 36. Installing $ sudo pear channel-discover pear.phpspec.net $ sudo pear install --alldeps phpspec/PHPSpecSaturday, 29 October 2011
  37. 37. “Specification, not verification” (Uncle Bob) $this->assertEquals(0, $result); becomes $result->should->be(0);Saturday, 29 October 2011
  38. 38. class CalculatorTest becomes class DescribeCalculatorSaturday, 29 October 2011
  39. 39. function testAddWithNoArguments() becomes function itReturnsZeroWithNoArguments()Saturday, 29 October 2011
  40. 40. All together class DescribeStringCalculator extends PHPSpecContext { function itReturnsZeroWithNoArguments() { $calculator = $this->spec(new StringCalculator); $result = $calculator->add(); $result->should->be(0); } }Saturday, 29 October 2011
  41. 41. Hooks before() after() beforeAll() afterAll()Saturday, 29 October 2011
  42. 42. Setting initial state with before hook class DescribeStringCalculator extends PHPSpecContext { function before() { $this->calculator = $this->spec(new StringCalculator); } function itReturnsZeroWithNoArguments() { $result = $this->calculator->add(); $result->should->be(0); } }Saturday, 29 October 2011
  43. 43. class DescribeStringCalculator extends PHPSpecContext { 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
  44. 44. Formatters progress documentation html coming soon: junitSaturday, 29 October 2011
  45. 45. Progress Formatter $ 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 seconds 4 examples, 1 failure, 1 pendingSaturday, 29 October 2011
  46. 46. HTML FormatterSaturday, 29 October 2011
  47. 47. Documentation FormatterSaturday, 29 October 2011
  48. 48. MatchersSaturday, 29 October 2011
  49. 49. be($match) equal($match) beEqualTo($match) beAnInstanceOf($match) beEmpty() beFalse() beGreaterThan($match) beGreaterThanOrEqualTo($match)Saturday, 29 October 2011
  50. 50. And more matchers...Saturday, 29 October 2011
  51. 51. beInteger() beLessThan($match) beLessThanOrEqualTo($match) beNull() beString() beTrue() throwException($match)Saturday, 29 October 2011
  52. 52. Predicate MatchersSaturday, 29 October 2011
  53. 53. $cell = $this->spec(new Cell); $cell->should->beAlive(); class Cell { protected $alive = true; public function isAlive() { return $this->alive; } ... }Saturday, 29 October 2011
  54. 54. $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
  55. 55. Custom MatchersSaturday, 29 October 2011
  56. 56. PHPSpecMatcherdefine(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 PHPSpecContext { ... function itAddsNewCourses() { $john = new Supervisor("John Smith"); $john->addToTeam($this->supportEngineer); $this->supportEngineer->should->reportTo($john); } }Saturday, 29 October 2011
  57. 57. PHPSpec &Saturday, 29 October 2011
  58. 58. Tool framework.zend.comSaturday, 29 October 2011
  59. 59. Installing $ sudo pear channel-discover pear.zfcampus.org $ sudo pear install zfcampus/zf $ zf create config $ vi ~/.zf.iniSaturday, 29 October 2011
  60. 60. .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"Saturday, 29 October 2011
  61. 61. create a project $ zf create project callconf Creating project at /var/www/callconf Note: This command created a web project, for more information setting up your VHOST, please see docs/READMESaturday, 29 October 2011
  62. 62. initialize PHPSpec $ cd callconf $ zf generate phpspec create spec create spec/SpecHelper.php create spec/.phpspec create spec/models create spec/views create spec/controllersSaturday, 29 October 2011
  63. 63. initialize Behat $ 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 hereSaturday, 29 October 2011
  64. 64. Saturday, 29 October 2011
  65. 65. ViewsSaturday, 29 October 2011
  66. 66. why specify the view?Saturday, 29 October 2011
  67. 67. Controller and model to the point Ensure we are focused on what matters Sustainable paceSaturday, 29 October 2011
  68. 68. create a view specSaturday, 29 October 2011
  69. 69. create a view spec $ zf create view-spec add CallForPapersSaturday, 29 October 2011
  70. 70. create a view spec $ zf create view-spec add CallForPapers Creating a view script in location /var/www/callconf/ application/views/scripts/call-for-papers/add.phtml Creating a spec at /var/www/callconf/spec/views/call-for- papers/AddSpec.phpSaturday, 29 October 2011
  71. 71. Saturday, 29 October 2011
  72. 72. Spec created by default <?php namespace CallForPapers; require_once __DIR__ . /../../SpecHelper.php; use PHPSpecContextZendView as ViewContext; class DescribeAdd extends ViewContext { function itRendersTheDefaultContent() { $this->render(); $this->rendered->should->contain(CallForPapers); $this->rendered->should->contain(add); } }Saturday, 29 October 2011
  73. 73. what behaviors can we describe in the view spec?Saturday, 29 October 2011
  74. 74. Variables we need assigned What content was rendered (We can use selectors)Saturday, 29 October 2011
  75. 75. 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
  76. 76. ControllersSaturday, 29 October 2011
  77. 77. create a controller specSaturday, 29 October 2011
  78. 78. create a controller spec $ zf create controller-spec CallForPapers add,createSaturday, 29 October 2011
  79. 79. create a controller spec $ zf create controller-spec CallForPapers add,create Creating a controller at /private/var/www/callconf/ application/controllers/CallForPapersController.php Creating an add action method in controller CallForPapers Creating an create action method in controller CallForPapers Creating a spec at /private/var/www/callconf/spec/ controllers/CallForPapersSpec.phpSaturday, 29 October 2011
  80. 80. Saturday, 29 October 2011
  81. 81. Spec created by default <?php require_once __DIR__ . /../SpecHelper.php; class DescribeCallForPapers extends PHPSpecContextZendController { function itShouldBeSuccessfulToGetAdd() { $this->get(call-for-papers/add); $this->response->should->beSuccess(); } }Saturday, 29 October 2011
  82. 82. what behaviors do we want to describe in the controller spec?Saturday, 29 October 2011
  83. 83. How do we want to route to its actions What view variables we need assigned What view we want renderedSaturday, 29 October 2011
  84. 84. 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 ); }Saturday, 29 October 2011
  85. 85. ModelsSaturday, 29 October 2011
  86. 86. create a model specSaturday, 29 October 2011
  87. 87. create a model spec $ zf create model-spec Speaker name:string,email:stringSaturday, 29 October 2011
  88. 88. create a model spec $ zf create model-spec Speaker name:string,email:string Creating a model at /private/var/www/callconf/application/ models/Speaker.php Creating a db table at /private/var/www/callconf/ application/models/DbTable/Speakers.php Creating a mapper at /private/var/www/callconf/application/ models/SpeakerMapper.php Creating a spec at /private/var/www/callconf/spec/models/ SpeakerSpec.php Creating migration scripts at /private/var/www/callconf/db/ migrate/001-CreateSpeakersTable.php Updating project profile /private/var/www/ callconf/.zfproject.xmlSaturday, 29 October 2011
  89. 89. Saturday, 29 October 2011
  90. 90. Spec created by default<?phprequire_once __DIR__ . /../SpecHelper.php;use Application_Model_Speaker as Speaker;class DescribeSpeaker extends PHPSpecContext{ 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
  91. 91. what behaviors can we describe in the model spec?Saturday, 29 October 2011
  92. 92. Business logic Validation Spying results from data source operationsSaturday, 29 October 2011
  93. 93. Business Logicclass DescribeSpeaker extends PHPSpecContext{ 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
  94. 94. Business Logicclass Speaker{ //... other methods function hasExtraPoints() { return stripos($this->getDietRestrictions(), vegetarian) !== false; }}Saturday, 29 October 2011
  95. 95. Real database hits?Saturday, 29 October 2011
  96. 96. Avoid Sometimes, for confidence When testing data access objectsSaturday, 29 October 2011
  97. 97. Dependency chainsSaturday, 29 October 2011
  98. 98. Dependencies can be hard to manageclass DescribeEvent extends PHPSpecContext{ 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
  99. 99. Usually dependencies are replaced with doubles when writing specs We can use a framework like Mockery But if you really need the real thingSaturday, 29 October 2011
  100. 100. Object MotherSaturday, 29 October 2011
  101. 101. Dependencies can be hard to manageclass DescribeEvent extends PHPSpecContext{ function itDoesSomethingWhenYouHaveSpeakerAllocated() { $exampleEvent = ExampleEvent::newWithSimultaneousSpeakers(); // specify expected event behavior }}Saturday, 29 October 2011
  102. 102. Code duplication Too many methodsSaturday, 29 October 2011
  103. 103. Test Data BuilderSaturday, 29 October 2011
  104. 104. Is created with save “empty” objects Has a fluent interface Has a build methodSaturday, 29 October 2011
  105. 105. Dependencies can be hard to manageclass DescribeEvent extends PHPSpecContext{ function itDoesSomethingWhenYouHaveSpeakerAllocated() { $eventBuilder = new EventBuilder(); $organizerBuilder = new OrganizerBuilder(); $event = $eventBuilder->withOrganizer( $organizerBuilder->withOrganization()->build() )->withConflictingSpeakers() ->build(); // specify expected event behavior }}Saturday, 29 October 2011
  106. 106. phactory.orgSaturday, 29 October 2011
  107. 107. installingSaturday, 29 October 2011
  108. 108. installing $ sudo pear channel-discover pearhub.orgSaturday, 29 October 2011
  109. 109. installing $ sudo pear channel-discover pearhub.org $ sudo pear install pearhub/PhactorySaturday, 29 October 2011
  110. 110. Needs a Pdo connection Get from default adapterSaturday, 29 October 2011
  111. 111. Create a connection protected function _initPhactory() { Phactory::setConnection( Zend_Db_Table_Abstract::getDefaultAdapter()); return Phactory::getConnection(); }Saturday, 29 October 2011
  112. 112. Define table blueprints // spec/factories.php Phactory::define(speaker, array( name => John Smith, email => john@smith.com));Saturday, 29 October 2011
  113. 113. Create objects // in one of my specs $ben = Phactory::create(speaker, array(name => Rowan)); $rowan = Phactory::create(speaker, array(name => Ben)); // Phactory_Row objects echo $ben->name // prints BenSaturday, 29 October 2011
  114. 114. Questions? 223Saturday, 29 October 2011
  115. 115. Thank you! http://joind.in/4318 http://slidesha.re/tcGM93 Marcello Duarte @_md is hiring. Come talk to me. 224Saturday, 29 October 2011

×