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

Behat + Mink + PhantomJS = TEST ALL THE THINGS!

Behat + Mink + PhantomJS = TEST ALL THE THINGS!

Michelle Sanver

September 23, 2015
Tweet

More Decks by Michelle Sanver

Other Decks in Programming

Transcript

  1. @michellesanver I would have a picture of grumpy programmer here,

    but I didn’t want to pay the royalties.
  2. @michellesanver Gherkin Feature: List
 In order to use the shoppinglist


    As a website user
 I need to be able to see the shoppinglist and use its elements
 
 @anonymous
 Scenario: Visit shoppinglist page as anonymous
 Given I am not logged in
 And I am on profile “shopping lists"
 Then I should see “My shopping lists"
 And I should see “I don’t have an M-connect account yet“
 
 @loggedin
 Scenario: See the shoppinglist
 Given I am logged in
 And I am on profile “shopping lists"
 Then I should see "My shopping lists"
 And I should see "I don’t have an M-connect account yet"
  3. @michellesanver Gherkin: Feature Feature: List
 In order to use the

    shoppinglist
 As a website user
 I need to be able to see the shoppinglist and use its elements
 
 @anonymous
 Scenario: Visit shoppinglist page as anonymous
 Given I am not logged in
 And I am on profile “shopping lists"
 Then I should see “My shopping lists"
 And I should see “I don’t have an M-connect account yet“
 
 @loggedin
 Scenario: See the shoppinglist
 Given I am logged in
 And I am on profile “shopping lists"
 Then I should see “My shopping lists"
 And I should see “Active lists"
  4. @michellesanver Gherkin: Scenario Feature: List
 In order to use the

    shoppinglist
 As a website user
 I need to be able to see the shoppinglist and use its elements
 
 Scenario: Larry garfield at a conference
 Given I am Larry Garfield
 And I am at a conference
 Then I should wear “Blue shirt”
 And I should wear “Leather vest"
 
 @loggedin
 Scenario: See the shoppinglist
 Given I am logged in
 And I am on profile “shopping lists"
 Then I should see “My shopping lists"
 And I should see “Active lists"
  5. @michellesanver Gherkin Given, Whens, Thens Scenario: Larry garfield at a

    conference
 Given I am Larry Garfield
 And I am at a conference
 Then I should wear “Blue shirt”
 And I should wear “Leather vest"
  6. @michellesanver Gherkin And + But Scenario: Larry garfield at a

    conference
 Given I am Larry Garfield
 And I am at a conference
 Then I should wear “Blue shirt”
 And I should wear “Leather vest"
  7. @michellesanver Gherkin: Background Feature: Multiple site support
 
 Background:
 Given

    a global administrator named "Greg"
 And a blog named "Greg's anti-tax rants"
 And a customer named "Wilson"
 And a blog named "Expensive Therapy" owned by "Wilson"
 
 Scenario: Wilson posts to his own blog
 Given I am logged in as Wilson
 When I try to post to "Expensive Therapy"
 Then I should see "Your article was published."
 
 Scenario: Greg posts to a client's blog
 Given I am logged in as Greg
 When I try to post to "Expensive Therapy"
 Then I should see "Your article was published."
  8. @michellesanver Gherkin: Scenario Outlines Scenario: Drink 5 out of 12


    Given there are 12 beers
 When I drink 5 beers
 Then I should have 7 beers
 
 Scenario: Drink 5 out of 20
 Given there are 20 beers
 When I drink 5 beers
 Then I should have 15 beers
  9. @michellesanver Gherkin: Scenario Outlines Scenario Outline: PHPDay social
 Given there

    are <start> beers
 When I drink <drink> beers
 Then I should have <left> beers
 
 Examples:
 | start | drink | left |
 | 12 | 5 | 7 |
 | 20 | 5 | 15 | Keep it DRY
  10. @michellesanver Steps use Behat\Behat\Context\SnippetAcceptingContext;
 use Behat\Gherkin\Node\PyStringNode;
 use Behat\Gherkin\Node\TableNode;
 
 class

    FeatureContext implements SnippetAcceptingContext
 {
 /**
 * Initializes context.
 */
 public function __construct()
 {
 }
 }
  11. @michellesanver Hooks /** 
 * @BeforeScenario
 */
 public function before(BeforeScenarioScope

    $scope)
 {
 if($this->getMink()->getDefaultSessionName() == 'selenium2') {
 $this->getSession()->resizeWindow(1200, 768);
 } else {
 self::$client = $this->getSession(‘symfony2') ->getDriver()->getClient();
 self::$container = self::$client->getContainer();
 self::$guzzle = self::$container->get(‘guzzle.api.client’);
 }
 }
  12. @michellesanver Tagged hooks /**
 * @BeforeScenario @database,@orm
 */
 public function

    cleanDatabase()
 {
 // clean database before
 // @database OR @orm scenarios
 }
  13. @michellesanver Tagged hooks: && /**
 * @BeforeScenario @database&&@fixtures
 */
 public

    function cleanDatabase()
 {
 // clean database before
 // @database OR @orm scenarios
 }
  14. @michellesanver Context /**
 * @When I do something with :argument


    */
 public function iDoSomethingWith($argument)
 {
 // do something with $argument
 }
  15. @michellesanver Context lifetime 2. Every step in a single scenario

    is executed inside a common context instance.
  16. @michellesanver Mink // wait for animation
 $session->wait(500); 
 // get

    loginlink
 $loginLinkSelector = '.hidden-phone a.login';
 $loginLink = $page->find('css', $loginLinkSelector); 
 // click loginlink
 $loginLink->click();
  17. @michellesanver PhantomJS @javascript
 Scenario: Searching for "tomaten"
 Given I am

    on "/sortiment/groceries"
 When I search for "tomatoes"
 Then I should see “Your search for tomatoes yielded"

  18. @michellesanver PhantomJS /**
 * @When I search for :searchterm
 */


    public function iSearchFor($searchterm)
 {
 
 }
  19. @michellesanver PhantomJS $session = $this->getSession();
 $page = $session->getPage();
 
 $searchField

    = $page->find('css', '.hidden-phone .search-input');
 $searchField->click();
 $searchField->setValue($searchterm);
 
 $searchButton = $page->find('css', '.hidden-phone .search-btn');
 $searchButton->click();
 
 // Wait until we have a search-title with a length longer than 0.
 $session->wait(
 0,
 "$('.search-title').length > 0"
 );
 
 $this->saveScreenshot('debugscreenshot.png', self::DEBUGPATH);

  20. @michellesanver PhantomJS $session = $this->getSession();
 $page = $session->getPage();
 
 $searchField

    = $page->find('css', '.hidden-phone .search-input');
 $searchField->click();
 $searchField->setValue($searchterm);
 
 $searchButton = $page->find('css', '.hidden-phone .search-btn');
 $searchButton->click();
 
 // Wait until we have a search-title with a length longer than 0.
 $session->wait(
 0,
 "$('.search-title').length > 0"
 );
 
 $this->saveScreenshot('debugscreenshot.png', self::DEBUGPATH);

  21. @michellesanver PhantomJS $session = $this->getSession();
 $page = $session->getPage();
 
 $searchField

    = $page->find('css', '.hidden-phone .search-input');
 $searchField->click();
 $searchField->setValue($searchterm);
 
 $searchButton = $page->find('css', '.hidden-phone .search-btn');
 $searchButton->click();
 
 // Wait until we have a search-title with a length longer than 0.
 $session->wait(
 0,
 "$('.search-title').length > 0"
 );
 
 $this->saveScreenshot('debugscreenshot.png', self::DEBUGPATH);

  22. @michellesanver PhantomJS $session = $this->getSession();
 $page = $session->getPage();
 
 $searchField

    = $page->find('css', '.hidden-phone .search-input');
 $searchField->click();
 $searchField->setValue($searchterm);
 
 $searchButton = $page->find('css', '.hidden-phone .search-btn');
 $searchButton->click();
 
 // Wait until we have a search-title with a length longer than 0.
 $session->wait(
 0,
 "$('.search-title').length > 0"
 );
 
 $this->saveScreenshot('debugscreenshot.png', self::DEBUGPATH);

  23. @michellesanver PhantomJS $session = $this->getSession();
 $page = $session->getPage();
 
 $searchField

    = $page->find('css', '.hidden-phone .search-input');
 $searchField->click();
 $searchField->setValue($searchterm);
 
 $searchButton = $page->find('css', '.hidden-phone .search-btn');
 $searchButton->click();
 
 // Wait until we have a search-title with a length longer than 0.
 $session->wait(
 0,
 "$('.search-title').length > 0"
 );
 
 $this->saveScreenshot('debugscreenshot.png', self::DEBUGPATH);

  24. @michellesanver Contexts and Steps Steps are your tests living in

    a context, which contains your logic for testing
  25. @michellesanver Testing JavaScript Use Mink with a driver, for instance

    PhantomJS to test dynamic parts of your website.
  26. @michellesanver Got a bit excited about one Testing Framework for

    ALL THE THINGS? As developers in an open source community the decision is ours.