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
Tweet

More Decks by Alistair Stead

Other Decks in Technology

Transcript

  1. BDD WITH
    MAGENTO
    Getting the conversation right!

    View Slide

  2. About us
    Marcello Duarte
    Head of Training @ Session Digital
    @_md
    Alistair Stead
    Technical Assurance Manager @ Session Digital
    @alistairstead

    View Slide

  3. Where did this start?

    View Slide

  4. • New feature requests
    • Unclear requirements
    • Not intuitive to Magento domain
    • Layers of communication add confusion
    The problem

    View Slide

  5. Build what the Customer wants!
    The problem

    View Slide

  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.

    View Slide

  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

    View Slide

  8. • Real simple requirement!
    • I will get that done in no time!
    Developer thinks...

    View Slide

  9. Large separation between
    Customer & Developer
    Risk

    View Slide

  10. Client Confirms
    Hi  John,
    That’s  great!  Yes  please,  add  that  to  the  next  release.
    Regards,
    Susan
    e-­‐Commerce  Director
    Acme  Furniture,  Ltd.

    View Slide

  11. Done?
    Deploy the code to UAT...

    View Slide

  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

    View Slide

  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.

    View Slide

  14. Not a new problem...
    68% failed projects
    [Standish Group Report 2009]

    View Slide

  15. Waterfall process
    9 months 3 months 2 months 12 months ...
    Requirement
    Analysis
    Design
    Code
    Test

    View Slide

  16. • Actually talk to the customer
    • “Automate” the conversation
    • Deliver often and refine
    Agile 2001

    View Slide

  17. TDD/Agile cycle
    ~20 minutes
    Requirement
    Acceptance
    Refactor
    Code
    Test

    View Slide

  18. Test === Get clear
    Extreme Programming === Sensible Programming
    Good solution, poor nameing

    View Slide

  19. • Clearing up some language issues
    • Make the feedback loop even shorter
    BDD

    View Slide

  20. BDD cycle
    Narratives
    (Why?)
    Emergent
    Design
    Implement
    Describe
    EXamples
    (What do you mean?)
    How

    View Slide

  21. General Tools
    Conversation === Behat
    Implementation === PHPSpec

    View Slide

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

    View Slide

  23. Create narative
    Why?
    Feature name, Actor, behavior, benefit
    What do you mean?
    Scenarios (made of steps)

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  28. Tools
    Automate the conversation

    View Slide

  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"
    }

    View Slide

  30. Install
    $  curl  http://getcomposer.org/  |  php
    $  ./composer.phar  install  -­‐-­‐dev

    View Slide

  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

    View Slide

  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

    View Slide

  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();
           }
           /**

    View Slide

  34. Append snippets
    $  bin/behat  -­‐-­‐append-­‐snippets

    View Slide

  35. behat.yml
    default:
           extensions:
                   MageTest\MagentoExtension\Extension:
                           base_url:  "http://test.magento.loc"

    View Slide

  36. Add an Admin Context
    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));
           }
    }

    View Slide

  37. Add an Admin Context
    use  Behat\Behat\Exception\PendingException;
    use  Behat\Gherkin\Node\TableNode;
    use  MageTest\MagentoExtension\Context\MagentoContext;
    class  AdminUserContext  extends  MagentoContext
    {
             //  paste  snippets  here
    }

    View Slide

  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()

    View Slide

  39. Code
    Time to write code?
    Not quite yet!

    View Slide

  40. Describing code
    Object Expectation Matcher
    $result                      -­‐>should                    Be....()
                                       -­‐>shouldNot

    View Slide

  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"
    }

    View Slide

  42. Composer Update
    $  ./composer.phar  update  -­‐-­‐dev

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  46. Describe
    $installer-­‐>afterApplyAllUpdates(function()  use  ($mage)  {
           $mage-­‐>getModel('catalog/product')
                     -­‐>getAttributes()
                     -­‐>shouldContainAttribute('accept_reviews');
    });

    View Slide

  47. Run PHPSpec
    $  bin/phpspec  run

    View Slide

  48. Make it pass
    $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();

    View Slide

  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()

    View Slide

  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()

    View Slide

  51. Client Response
    Hi  John,
    That’s  exactly  what  I  wanted.  Thank  you!
    Regards,
    Susan
    e-­‐Commerce  Director
    Acme  Furniture,  Ltd.

    View Slide

  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');
           }

    View Slide

  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');
           }
    }

    View Slide

  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');
           }
    }

    View Slide

  55. Benefits
    Regression mechanism for free!

    View Slide

  56. Benefits
    Documentation for free!

    View Slide

  57. Benefits
    Automation & Generation of code

    View Slide

  58. Benefits
    A very short feedback loop!

    View Slide

  59. Benefits
    Happy Customer!

    View Slide

  60. Benefits
    Happy Developers!

    View Slide

  61. Questions?
    Thank you!
    @alistairstead

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide