Upgrade to Pro — share decks privately, control downloads, hide ads and more …

BDD with Magento

BDD with Magento

An introduction and step by step example of using Behat and PHPSpec2 with Magento to develop a sample feature. Using the two Open Source extensions BehatMage and MageSpec to make the tools aware of the the Magento domain.


Alistair Stead

November 13, 2012


  1. BDD WITH MAGENTO Getting the conversation right!

  2. About us Marcello Duarte Head of Training @ Session Digital

    @_md Alistair Stead Technical Assurance Manager @ Session Digital @alistairstead
  3. Where did this start?

  4. • New feature requests • Unclear requirements • Not intuitive

    to Magento domain • Layers of communication add confusion The problem
  5. Build what the Customer wants! The problem

  6. Client Request Hi  John, Does  Magento  have  a  feature  to

     add  reviews  to  products?  If  not,  how   much  would  cost  to  implement  it? Regards, Susan e-­‐Commerce  Director Acme  Furniture,  Ltd.
  7. Intial reaction Hi  Susan, It’s  out  of  the  box  :)

     Should  we  enable  this  funcGon  in  the  next   release? John Project  Manager MageWoders  Development
  8. • Real simple requirement! • I will get that done

    in no time! Developer thinks...
  9. Large separation between Customer & Developer Risk

  10. Client Confirms Hi  John, That’s  great!  Yes  please,  add  that

     to  the  next  release. Regards, Susan e-­‐Commerce  Director Acme  Furniture,  Ltd.
  11. Done? Deploy the code to UAT...

  12. Let the client know Hi  Susan, We  have  deployed  the

     review  funcGonality  to  the  UAT  server  for  your   acceptance.  I  trust  this  meets  your  requirement. John Project  Manager MageWoders  Development
  13. Client response Hi  John, Thanks  for  the  update.  I  also

     wanted  to  be  able  to  turn  this  feature   off  or  on  for  products.  I  don’t  seem  to  be  able  to  do  this? Can  we  have  a  conference  call  with  the  enGre  team  to  resolve  this?   This  is  URGENT  we  can’t  launch  a  system  like  this! Regards, Susan e-­‐Commerce  Director Acme  Furniture,  Ltd.
  14. Not a new problem... 68% failed projects [Standish Group Report

  15. Waterfall process 9 months 3 months 2 months 12 months

    ... Requirement Analysis Design Code Test
  16. • Actually talk to the customer • “Automate” the conversation

    • Deliver often and refine Agile 2001
  17. TDD/Agile cycle ~20 minutes Requirement Acceptance Refactor Code Test

  18. Test === Get clear Extreme Programming === Sensible Programming Good

    solution, poor nameing
  19. • Clearing up some language issues • Make the feedback

    loop even shorter BDD
  20. BDD cycle Narratives (Why?) Emergent Design Implement Describe EXamples (What

    do you mean?) How
  21. General Tools Conversation === Behat Implementation === PHPSpec

  22. Domain Tools Conversation === BehatMage Implementation === MageSpec

  23. Create narative Why? Feature name, Actor, behavior, benefit What do

    you mean? Scenarios (made of steps)
  24. Refactor email Feature:  Admin  User  can  manage  review  visibility  

     So  that  our  Customers  are  not  influenced  by  a  product  with  bad  review  history,    as  an  Admin  User    I  want  to  disable  reviews  of  those  specific  products
  25. Add a scenario    Scenario:  Turn  off  reviews  per  product

           Given  the  following  products  exist:            |  sku            |  name                      |  accepts_reviews  |            |  Ottoman1  |  Ottoman                |  1                              |        And  "Ottoman1"  has  existing  reviews        When  I  turn  this  off  for  "Ottoman1"  product        Then  no  review  should  be  displayed
  26. Add another for clarity    Scenario:  Turning  off  reviews  will

     not  affect  other  products        Given  the  following  products  exist:            |  sku            |  name                      |  accepts_reviews  |            |  Ottoman1  |  Ottoman                |  1                              |            |  Chair1      |  Chair                    |  1                              |        And  "Ottoman1"  has  existing  reviews        And  "Chair"  has  existing  reviews        When  I  turn  this  off  for  "Ottoman1"  product        Then  "Chair"  can  still  accept  reviews
  27. Feature Feature:  Admin  User  can  manage  review  visibility    So

     that  our  Customers  are  not  influenced  by  a  product  with  bad  review  history,    as  an  Admin  User    I  want  to  disable  reviews  of  those  specific  products    Scenario:  Turn  off  reviews  per  product        Given  the  following  products  exist:            |  sku            |  name                      |  accepts_reviews  |            |  Ottoman1  |  Ottoman                |  1                              |        And  "Ottoman1"  has  existing  reviews        When  I  turn  this  off  for  "Ottoman1"  product        Then  no  review  should  be  displayed    Scenario:  Turning  off  reviews  will  not  affect  other  products        Given  the  following  products  exist:            |  sku            |  name                      |  accepts_reviews  |            |  Ottoman1  |  Ottoman                |  1                              |            |  Chair1      |  Chair                    |  1                              |        And  "Ottoman1"  has  existing  reviews        And  "Chair"  has  existing  reviews        When  I  turn  this  off  for  "Ottoman1"  product        Then  "Chair"  can  still  accept  reviews
  28. Tools Automate the conversation

  29. composer.json {        "require-­‐dev":  {      

             "magetest/magento-­‐behat-­‐extension":  "dev-­‐develop",        },        "config":  {                "bin-­‐dir":  "bin"        },        "autoload":  {                "psr-­‐0":  {                        "":  [                                "app",                                "app/code/local",                                "app/code/community",                                "app/code/core",                                "lib"                        ]                }        },        "minimum-­‐stability":  "dev" }
  30. Install $  curl  http://getcomposer.org/  |  php $  ./composer.phar  install  -­‐-­‐dev

  31. Initialize Behat $  bin/behat  -­‐-­‐init +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
  32. Run Behat $  bin/behat Feature:  Admin  User  can  manage  review

     visibility    So  that  our  Customers  are  not  influenced  by  a  product  with  bad  review  history,    as  an  Admin  User    I  want  to  disable  reviews  of  those  specific  products    Scenario:  Turn  off  reviews  per  product                            #  features/reviews/admin_user_manages_review_visibility        Given  the  following  products  exist:            |  sku            |  name        |  accepts_reviews  |            |  Ottoman1  |  Ottoman  |  1                              |        And  "Ottoman1"  has  existing  reviews        When  I  turn  reviews  off  for  "Ottoman1"  product        Then  no  review  should  be  displayed  for  "Ottoman1"    Scenario:  Turning  off  reviews  will  not  affect  other  products  #  features/reviews/admin_user_manages_review_v        Given  the  following  products  exist:            |  sku            |  name        |  accepts_reviews  |            |  Ottoman1  |  Ottoman  |  1                              |            |  Chair1      |  Chair      |  1                              |        And  "Ottoman1"  has  existing  reviews        And  "Chair1"  has  existing  reviews        When  I  turn  reviews  off  for  "Ottoman1"  product        Then  "Chair1"  can  still  accept  reviews
  33. Run Behat You  can  implement  step  definitions  for  undefined  steps

     with  these  snippets:        /**          *  @Given  /^the  following  products  exist:$/          */        public  function  theFollowingProductsExist(TableNode  $table)        {                throw  new  PendingException();        }        /**          *  @Given  /^"([^"]*)"  has  existing  reviews$/          */        public  function  hasExistingReviews($arg1)        {                throw  new  PendingException();        }        /**          *  @When  /^I  turn  reviews  off  for  "([^"]*)"  product$/          */        public  function  iTurnReviewsOffForProduct($arg1)        {                throw  new  PendingException();        }        /**
  34. Append snippets $  bin/behat  -­‐-­‐append-­‐snippets

  35. behat.yml default:        extensions:        

           MageTest\MagentoExtension\Extension:                        base_url:  "http://test.magento.loc"
  36. Add an Admin Context <?php use  Behat\Behat\Context\ClosuredContextInterface,      

     Behat\Behat\Context\TranslatedContextInterface,        Behat\Behat\Context\BehatContext,        Behat\Behat\Exception\PendingException; use  Behat\Gherkin\Node\PyStringNode,        Behat\Gherkin\Node\TableNode; use  MageTest\MagentoExtension\Context\MagentoContext; /**  *  Features  context.  */ class  FeatureContext  extends  BehatContext {        public  function  __construct($paramters)        {                $this-­‐>useContext('admin_user',  new  AdminUserContext($parameters));        } }
  37. Add an Admin Context <?php use  Behat\Behat\Exception\PendingException; use  Behat\Gherkin\Node\TableNode; use

     MageTest\MagentoExtension\Context\MagentoContext; class  AdminUserContext  extends  MagentoContext {          //  paste  snippets  here }
  38. With Magento Context $  bin/behat Feature:  Admin  User  can  manage

     review  visibility    So  that  our  Customers  are  not  influenced  by  a  product  with  bad  review  history,    as  an  Admin  User    I  want  to  disable  reviews  of  those  specific  products    Scenario:  Turn  off  reviews  per  product                            #  features/reviews/admin_user_manages_review_visibi        Given  the  following  products  exist:                              #  AdminUserContext::theProductsExist()            |  sku            |  name        |  accepts_reviews  |            |  Ottoman1  |  Ottoman  |  1                              |            accepts_reviews  is  not  yet  defined  as  an  attribute  of  Product        And  "Ottoman1"  has  existing  reviews                              #  AdminUserContext::hasExistingReviews()        When  I  turn  reviews  off  for  "Ottoman1"  product        #  AdminUserContext::iTurnReviewsOffForProduct()        Then  no  review  should  be  displayed  for  "Ottoman1"  #  AdminUserContext::noReviewShouldBeDisplayedFor()    Scenario:  Turning  off  reviews  will  not  affect  other  products  #  features/reviews/admin_user_manages_revi        Given  the  following  products  exist:                                                #  AdminUserContext::theProductsExist()            |  sku            |  name        |  accepts_reviews  |            |  Ottoman1  |  Ottoman  |  1                              |            |  Chair1      |  Chair      |  1                              |            accepts_reviews  is  not  yet  defined  as  an  attribute  of  Product        And  "Ottoman1"  has  existing  reviews                                                #  AdminUserContext::hasExistingReviews()        And  "Chair1"  has  existing  reviews                                                    #  AdminUserContext::hasExistingReviews()
  39. Code Time to write code? Not quite yet!

  40. Describing code Object Expectation Matcher $result        

                 -­‐>should                    Be....()                                    -­‐>shouldNot
  41. Add PHPSpec {        "require-­‐dev":  {    

               "magetest/magento-­‐behat-­‐extension":  "dev-­‐develop",                "magetest/magento-­‐phpspec-­‐extension":  "*"        },        "config":  {                "bin-­‐dir":  "bin"        },        "autoload":  {                "psr-­‐0":  {                        "MageTest":  "src/",                        "":  [                                "app",                                "app/code/local",                                "app/code/community",                                "app/code/core",                                "lib"                        ]                }        },        "minimum-­‐stability":  "dev" }
  42. Composer Update $  ./composer.phar  update  -­‐-­‐dev

  43. phpspec.yml extensions:  [MageTest\PHPSpec2\MagentoExtension\Extension] magento_root:  "./public"

  44. Describe $  bin/phpspec  describe:setup-­‐script  Acme_Catalog_Setup

  45. Describe $  vim  spec/Acme/Catalog/sql/install-­‐0.1.0.php

  46. Describe <?php $installer-­‐>afterApplyAllUpdates(function()  use  ($mage)  {        $mage-­‐>getModel('catalog/product')

                     -­‐>getAttributes()                  -­‐>shouldContainAttribute('accept_reviews'); });
  47. Run PHPSpec $  bin/phpspec  run

  48. Make it pass <?php $installer  =  $this; $installer-­‐>startSetup(); $installer-­‐>addAttribute('catalog_product',  'accepts_reviews',

     array(        'group'  =>  'General',        'input'  =>  'yesno',        'type'  =>  'int',        'label'  =>  'Accepts  Reviews',        'backend'  =>  '',        'default'  =>  1,        'visible'  =>  1,        'required'  =>  1,        'user_defined'  =>  1,        'searchable'  =>  0,        'filterable'  =>  0,        'comparable'  =>  0,        'visible_on_front'  =>  1,        'visible_in_advanced_search'  =>  0,        'is_html_allowed_on_front'  =>  0,        'global'  =>  Mage_Catalog_Model_Resource_Eav_Attribute::SCOPE_GLOBAL, )); $installer-­‐>entSetup();
  49. After attribute exists $  bin/behat Feature:  Admin  User  can  manage

     review  visibility    So  that  our  Customers  are  not  influenced  by  a  product  with  bad  review  history,    as  an  Admin  User    I  want  to  disable  reviews  of  those  specific  products    Scenario:  Turn  off  reviews  per  product                            #  features/reviews/admin_user_manages_review_visibi        Given  the  following  products  exist:                              #  AdminUserContext::theProductsExist()            |  sku            |  name        |  accepts_reviews  |            |  Ottoman1  |  Ottoman  |  1                              |        And  "Ottoman1"  has  existing  reviews                              #  AdminUserContext::hasExistingReviews()        When  I  turn  reviews  off  for  "Ottoman1"  product        #  AdminUserContext::iTurnReviewsOffForProduct()        Then  no  review  should  be  displayed  for  "Ottoman1"  #  AdminUserContext::noReviewShouldBeDisplayedFor()    Scenario:  Turning  off  reviews  will  not  affect  other  products  #  features/reviews/admin_user_manages_revi        Given  the  following  products  exist:                                                #  AdminUserContext::theProductsExist()            |  sku            |  name        |  accepts_reviews  |            |  Ottoman1  |  Ottoman  |  1                              |            |  Chair1      |  Chair      |  1                              |            accepts_reviews  is  not  yet  defined  as  an  attribute  of  Product        And  "Ottoman1"  has  existing  reviews                                                #  AdminUserContext::hasExistingReviews()        And  "Chair1"  has  existing  reviews                                                    #  AdminUserContext::hasExistingReviews()
  50. Repeat until it’s all green $  bin/behat Feature:  Admin  User

     can  manage  review  visibility    So  that  our  Customers  are  not  influenced  by  a  product  with  bad  review  history,    as  an  Admin  User    I  want  to  disable  reviews  of  those  specific  products    Scenario:  Turn  off  reviews  per  product                            #  features/reviews/admin_user_manages_review_visibi        Given  the  following  products  exist:                              #  AdminUserContext::theProductsExist()            |  sku            |  name        |  accepts_reviews  |            |  Ottoman1  |  Ottoman  |  1                              |        And  "Ottoman1"  has  existing  reviews                              #  AdminUserContext::hasExistingReviews()        When  I  turn  reviews  off  for  "Ottoman1"  product        #  AdminUserContext::iTurnReviewsOffForProduct()        Then  no  review  should  be  displayed  for  "Ottoman1"  #  AdminUserContext::noReviewShouldBeDisplayedFor()    Scenario:  Turning  off  reviews  will  not  affect  other  products  #  features/reviews/admin_user_manages_revi        Given  the  following  products  exist:                                                #  AdminUserContext::theProductsExist()            |  sku            |  name        |  accepts_reviews  |            |  Ottoman1  |  Ottoman  |  1                              |            |  Chair1      |  Chair      |  1                              |            accepts_reviews  is  not  yet  defined  as  an  attribute  of  Product        And  "Ottoman1"  has  existing  reviews                                                #  AdminUserContext::hasExistingReviews()        And  "Chair1"  has  existing  reviews                                                    #  AdminUserContext::hasExistingReviews()
  51. Client Response Hi  John, That’s  exactly  what  I  wanted.  Thank

     you! Regards, Susan e-­‐Commerce  Director Acme  Furniture,  Ltd.
  52. Describe Controllers class  Acme_Cms_IndexController  extends  ControllerBehavior {      

     function  it_wires_up_the_blocks_for()        {                $this-­‐>indexController-­‐>shouldHaveAction('listAction');                $this-­‐>listAction()-­‐>shouldCreateBlocks(                        array(                                'core/text_list',                                'core/template',                                'achme/widget'                        )                );                $this-­‐>listAction()-­‐>shouldRenderLayoutHandle('default');                $this-­‐>listAction()-­‐>shouldRenderTemplate('default.phtml');                $this-­‐>listAction()-­‐>shouldRespondWith('key',  'value',  'matcher?');                $this-­‐>listAction()-­‐>response-­‐>shouldHaveHeader('key',  'value');                $this-­‐>listAction()-­‐>response-­‐>shouldHaveStatus('http_response_code');                $this-­‐>listAction()-­‐>response-­‐>getBody()-­‐>shouldContain('regex');                $this-­‐>listAction()-­‐>shouldBeInArea('admin');                $this-­‐>listAction()-­‐>shouldTranslate('from')-­‐>to('to');                $this-­‐>listAction()-­‐>shouldDispatchEvent('event_name');        }
  53. Describe Blocks class  Acme_Cms_Block_Page  extends  BlockBehavior {      

     function  it_generates_a_block_for_layout()        {                $this-­‐>relatedProductsBlock-­‐>shouldBeOfType('reports/related_products');                $this-­‐>relatedProductsBlock-­‐>shouldHaveName('right.reposts.related_products');                $this-­‐>relatedProductsBlock-­‐>getFoo()-­‐>shouldReturn('value');                $this-­‐>relatedProductsBlock-­‐>toHtml()-­‐>shouldContain('regex');                $this-­‐>relatedProductsBlock-­‐>shouldRenderTemplate('releated_products.phtml');        } }
  54. Describe Helpers class  Acme_Cms_Helper_Data  extends  HelperBehavior {      

     function  it_provides_access_to_data_in_many_places_including_the_view()        {                $this-­‐>helper-­‐>shouldTranslate('from',  'to');                $this-­‐>helper-­‐>getFoo()-­‐>shouldReturn('value');        } }
  55. Benefits Regression mechanism for free!

  56. Benefits Documentation for free!

  57. Benefits Automation & Generation of code

  58. Benefits A very short feedback loop!

  59. Benefits Happy Customer!

  60. Benefits Happy Developers!

  61. Questions? Thank you! @alistairstead

  62. http:// bit.ly/magebdd Slides and links

  63. http://bit.ly/inviqa-bdd-training BDD training

  64. Come Join us... http://blog.sessiondigital.com/careers