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

More Decks by Alistair Stead

Other Decks in Technology


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

    @_md Alistair Stead Technical Assurance Manager @ Session Digital @alistairstead
  2. • New feature requests • Unclear requirements • Not intuitive

    to Magento domain • Layers of communication add confusion The problem
  3. 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.
  4. Intial reaction Hi  Susan, It’s  out  of  the  box  :)

     Should  we  enable  this  funcGon  in  the  next   release? John Project  Manager MageWoders  Development
  5. Client Confirms Hi  John, That’s  great!  Yes  please,  add  that

     to  the  next  release. Regards, Susan e-­‐Commerce  Director Acme  Furniture,  Ltd.
  6. 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
  7. 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.
  8. Waterfall process 9 months 3 months 2 months 12 months

    ... Requirement Analysis Design Code Test
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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" }
  14. 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
  15. 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
  16. 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();        }        /**
  17. behat.yml default:        extensions:        

           MageTest\MagentoExtension\Extension:                        base_url:  "http://test.magento.loc"
  18. 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));        } }
  19. 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 }
  20. 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()
  21. Describing code Object Expectation Matcher $result        

                 -­‐>should                    Be....()                                    -­‐>shouldNot
  22. 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" }
  23. Describe <?php $installer-­‐>afterApplyAllUpdates(function()  use  ($mage)  {        $mage-­‐>getModel('catalog/product')

                     -­‐>getAttributes()                  -­‐>shouldContainAttribute('accept_reviews'); });
  24. 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();
  25. 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()
  26. 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()
  27. Client Response Hi  John, That’s  exactly  what  I  wanted.  Thank

     you! Regards, Susan e-­‐Commerce  Director Acme  Furniture,  Ltd.
  28. 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');        }
  29. 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');        } }
  30. 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');        } }