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

Test legacy apps with Behat

Test legacy apps with Behat

A tutorial given at PHPNW15 - October 2nd 2015. All references and examples use the following repository and Vagrant VM environment: https://github.com/pavlakis/phpnw15-behat-tutorial

Antonis Pavlakis

October 02, 2015
Tweet

More Decks by Antonis Pavlakis

Other Decks in Programming

Transcript

  1. The environment • Legacy code - ZF1 (1.10.6) tutorial by

    Rob Allen (@akrabat) • Basic LAMP box with Ubuntu 12.04, PHP 5.5, MySQL 5.5
  2. Getting started (1) git clone [email protected]:pavlakis/phpnw15-behat-tutorial.git (2) vagrant up (3)

    Add the following to your hosts file: 192.168.42.200 dev.legacy.com test.legacy.com
  3. setting up Behat Create the directory structure. From inside the

    /var/www/tests/behat/ folder, run: > ../../vendor/bin/behat --init Step 2
  4. Step 3 default:
 suites:
 default:
 contexts:
 - FeatureContext
 
 extensions:


    
 Behat\MinkExtension:
 browser_name: phantomjs
 javascript_session: selenium2
 base_url: http://test.legacy.com/
 
 sessions:
 my_session:
 goutte:
 guzzle_parameters:
 verify: false
 
 selenium2:
 wd_host: "http://localhost:8643/wd/hub"
 capabilities: { "browser": "phantomjs" } Ensure the URL is accessible from your server. (e.g. in your hosts file) /var/www/tests/behat/behat.yml
  5. Features & Scenarios Behat tests live inside .feature files Behat

    tests are called Scenarios Behat Scenarios use the Gherkin language, which allows us to write English statements.
  6. Definitions A statement from a scenario that maps to a

    PHP method and identified through the method’s doc-block. For available definitions, run: $ ./behat --config=/var/www/tests/behat/behat.yml -dl
  7. “Scenario: The homepage loads” Feature: Albums
 I can create, edit

    and delete albums 
 Scenario: The homepage loads
 Given I am on the homepage
 Then I should see text matching "My Album" tests/behat/features/bootstrap/album.feature
  8. “Scenario: Page does not exist” Feature: Albums
 I can create,

    edit and delete albums
 
 Scenario: The homepage loads
 Given I am on the homepage
 Then I should see text matching "My Album” Scenario: Page does not exist Given I …
 Then I … tests/behat/features/bootstrap/album.feature
  9. “Scenario: I can create an album” • Possible steps: •

    Navigate to the “create” page • Add artist • Add album • Submit form
  10. “Scenario: I can create an album” Feature: Albums
 I can

    create, edit and delete albums
 
 Scenario: The homepage loads
 Given I am on the homepage
 Then I should see text matching “My Album” Scenario: Page does not exist Given I …
 Then I … Scenario: I can create an album
 Given I am on the homepage
 And I follow … tests/behat/features/bootstrap/album.feature
  11. “Scenario: I can create an album” tests/behat/features/bootstrap/album.feature Guidance. Use the

    following definitions: And /^(?:|I )follow "(?P<link>(?:[^"]|\\")*)"$/ And /^(?:|I )fill in "(?P<field>(?:[^"]|\\")*)" with "(?P<value>(?:[^"]|\\")*)"$/ And /^(?:|I )press "(?P<button>(?:[^"]|\\")*)"$/
  12. “A solution…” Feature: Albums
 I can create, edit and delete

    albums
 
 Scenario: The homepage loads
 Given I am on the homepage
 Then I should see text matching "My Album"
 
 Scenario: Page does not exist
 Given I am on "/notexist"
 Then I should see text matching "Page not found"
 
 Scenario: Can create an album
 Given I am on the homepage
 And I follow "Add new album"
 And I fill in "artist" with "Queen"
 And I fill in "title" with “A kind of magic"
 And I press "Add"
 Then I should see text matching "A kind of magic"
 And I should be on the homepage
 
 Scenario: Can edit an album
 Given I am on the homepage
 And I follow "Edit"
 And I fill in "artist" with “Billy Joel"
 And I fill in "title" with “An Innocent Man"
 And I press "Save"
 Then I should see text matching “An Innocent Man"
 And I should be on the homepage tests/behat/features/bootstrap/album.feature
  13. “Can create a default album” 
 
 Scenario: Can create

    a default album
 Given I am on the add page
 
 
 tests/behat/features/bootstrap/album.feature
  14. Custom step $ legacybehat ..................U------ 5 scenarios (4 passed, 1

    undefined) 25 steps (18 passed, 1 undefined, 6 skipped) 0m0.28s (14.25Mb) --- FeatureContext has missing steps. Define them with these snippets: /** * @Given I am on the add page */ public function iAmOnTheAddPage() { throw new PendingException(); } terminal
  15. Custom step definition /**
 * @Given I am on the

    add page
 */
 public function iAmOnTheAddPage()
 {
 throw new PendingException();
 }
 tests/behat/features/bootstrap/FeatureContext.php
  16. Custom step definition /**
 * @Given I am on the

    add page
 */
 public function iAmOnTheAddPage()
 {
 $this->visit('/index/add');
 } tests/behat/features/bootstrap/FeatureContext.php
  17. First iteration Scenario: Can create a default album
 Given I

    am on the add page
 And I fill in "artist" with "test123"
 And I fill in "title" with "test321"
 And I press "Add"
 Then I should see text matching "test123"
 And I should be on the homepage tests/behat/features/bootstrap/FeatureContext.php
  18. Second iteration Scenario: Can create a default album
 Given I

    am on the add page
 And I create a default album
 Then The album has been created tests/behat/features/bootstrap/FeatureContext.php
  19. Exercise #6 Guidance. Use the following methods: $this->fillField($field, $value); $this->pressButton($button);

    $this->assertPageContainsText($text); tests/behat/features/bootstrap/FeatureContext.php
  20. Traversing Elements Making sure you’re accessing the element you want.

    (Following, are examples from the http://mink.behat.org/ docs)
  21. Selectors css selector Using CSS expressions to search the DOM

    $title = $page->find('css', 'h1'); $buttonIcon = $page->find('css', '.btn > .icon');
  22. Selectors xpath selector Using path queries to search the DOM

    $anchorsWithoutUrl = $page->findAll('xpath', '// a[not(@href)]');
  23. Exercise #7 Using the CSS selector (1) Edit a specific

    album (2) Delete a specific album
  24. Can edit a specific album /**
 * @Given I go

    to edit album by title :title
 */
 public function iGoToEditAlbumByTitle($title)
 {
 $page = $this->getMink()->getSession()->getPage();
 
 $editLink = $page->find(…);
 
 $this->visit($editLink);
 } tests/behat/features/bootstrap/FeatureContext.php
  25. Can edit a specific album /**
 * @Given I go

    to edit album by title :title
 */
 public function iGoToEditAlbumByTitle($title)
 {
 $page = $this->getMink()->getSession()->getPage();
 
 $editLink = $page->find(
 'css',
 sprintf('table tr td:contains("%s")', $title)
 )
 ->getParent()
 ->findLink('Edit')
 ->getAttribute('href');
 
 $this->visit($editLink);
 } tests/behat/features/bootstrap/FeatureContext.php
  26. Hooks From docs.behat.org “Behat provides a number of hook points

    which allow us to run arbitrary logic at various points in the Behat test cycle.”
  27. Before Feature use Behat\Behat\Hook\Scope\BeforeFeatureScope; … /** @BeforeFeature */
 public static

    function prepareForTheFeature(BeforeFeatureScope $scope)
 {
 
 } tests/behat/features/bootstrap/FeatureContext.php
  28. Clean DB use Behat\Behat\Hook\Scope\BeforeFeatureScope; … /** @BeforeFeature */
 public static

    function cleanDB(BeforeFeatureScope $scope)
 {
 
 } tests/behat/features/bootstrap/FeatureContext.php
  29. Clean DB #1 use Behat\Behat\Hook\Scope\BeforeFeatureScope; … /** @BeforeFeature */
 public

    static function cleanDB(BeforeFeatureScope $scope)
 {
 
 // prepare system for the feature
 $username = 'root';
 $password = 'Admin123';
 $database = 'zf-tutorial-test';
 shell_exec("sh /var/www/scripts/db.sh clean {$username} {$password} {$database}");
 } tests/behat/features/bootstrap/FeatureContext.php
  30. default:
 suites:
 default:
 contexts:
 - FeatureContext:
 - root
 - Admin123


    - zf-tutorial-test
 
 extensions:
 
 Behat\MinkExtension:
 browser_name: phantomjs
 javascript_session: selenium2
 base_url: http://test.legacy.com/
 
 sessions:
 my_session:
 goutte:
 guzzle_parameters:
 verify: false
 
 selenium2:
 wd_host: "http://localhost:8643/wd/hub"
 capabilities: { "browser": "phantomjs" } /var/www/tests/behat/behat.yml /var/www/tests/behat/ features/bootstrap/ FeatureContext.php
  31. Clean DB #2 use Behat\Behat\Hook\Scope\BeforeFeatureScope; … /** @BeforeFeature */
 public

    static function cleanDB(BeforeFeatureScope $scope)
 {
 
 // prepare system for the feature
 $dbCreds = $scope->getSuite()->getSetting("contexts")[0]["FeatureContext"];
 
 shell_exec("sh /var/www/scripts/db.sh clean {$dbCreds[0]} {$dbCreds[1]} {$dbCreds[2]}");
 } tests/behat/features/bootstrap/FeatureContext.php
  32. Available Hooks BeforeSuite [Behat\Testwork\Hook\Scope\BeforeSuiteScope] AfterSuite [Behat\Testwork\Hook\Scope\AfterSuiteScope] BeforeFeature [Behat\Behat\Hook\Scope\BeforeFeatureScope] AfterFeature [Behat\Behat\Hook\Scope\AfterFeatureScope]

    BeforeScenario [Behat\Behat\Hook\Scope\BeforeScenarioScope] AfterScenario [Behat\Behat\Hook\Scope\AfterScenarioScope] BeforeStep [Behat\Behat\Hook\Scope\BeforeStepScope] AfterStep [Behat\Behat\Hook\Scope\AfterStepScope]
  33. Exercise #8 (1) Create a @BeforeFeature to populate with test

    data (fixtures/seeders) (2) Create an @AfterFeature to clean DB
  34. Exercise #9 (1) Create a PDO object (2) Create an

    album. Use PDO to verify it exists in the DB
  35. Connect to DB public function __construct($username, $password, $dbName)
 {
 $this->db

    = new \PDO("mysql:host=127.0.0.1;dbname={$dbName}", $username, $password);
 } tests/behat/features/bootstrap/FeatureContext.php
  36. Exercise #11 (1) Create 10 albums through the UI (2)

    Delete 5 albums. (3) Verify they have been deleted in the DB.
  37. Tags “Tags are a great way to organise your features

    and scenarios” - docs.behat.org
  38. Tags default:
 suites:
 default:
 contexts:
 - FeatureContext:
 - root
 -

    Admin123
 - zf-tutorial-test
 filters:
 tags: @db /var/www/tests/behat/behat.yml Add more using a comma-separated format.
  39. Tags - Features and Scenarios @db Feature: Albums
 I can

    create, edit and delete albums
 
 Scenario: Can create an album
 Given I am on the homepage
 And I follow "Add new album"
 And I fill in "artist" with "Queen"
 And I fill in "title" with “A kind of magic"
 And I press "Add"
 Then I should see text matching "A kind of magic"
 And I should be on the homepage
 @db
 Scenario: Can edit an album
 Given I am on the homepage
 And I follow "Edit"
 And I fill in "artist" with “Billy Joel"
 And I fill in "title" with “An Innocent Man"
 And I press "Save"
 Then I should see text matching “An Innocent Man"
 And I should be on the homepage tests/behat/features/bootstrap/album.feature
  40. Exercise #12 (1) Create a tag with a fixture to

    populate DB with a default album. (2) Create a scenario that edits the above album.
  41. “A Solution…” default:
 suites:
 default:
 contexts:
 - FeatureContext:
 - root


    - Admin123
 - zf-tutorial-test
 filters:
 tags: @defaultAlbumFixture,@db /var/www/tests/behat/behat.yml
  42. “A Solution…” @db Feature: Albums
 I can create, edit and

    delete albums
 
 Scenario: Can create an album
 Given I am on the homepage
 And I follow "Add new album"
 And I fill in "artist" with "Queen"
 And I fill in "title" with “A kind of magic"
 And I press "Add"
 Then I should see text matching "A kind of magic"
 And I should be on the homepage
 @defaultAlbumFixture
 Scenario: Can edit an album
 Given I am on the homepage
 And I follow "Edit"
 And I fill in "artist" with “Billy Joel"
 And I fill in "title" with “An Innocent Man"
 And I press "Save"
 Then I should see text matching “An Innocent Man"
 And I should be on the homepage tests/behat/features/bootstrap/album.feature
  43. “A Solution…” /**
 * @BeforeScenario @defaultAlbumFixture
 */
 public static function

    populateDB(BeforeScenarioScope $scope)
 {
 $dbCreds = $scope->getSuite()->getSetting("contexts")[0]["FeatureContext"];
 
 shell_exec("sh /var/www/scripts/db.sh populate {$dbCreds[0]} {$dbCreds[1]} {$dbCreds[2]}");
 }
 
 /**
 * @BeforeFeature @db
 */
 public static function cleanDB(BeforeFeatureScope $scope)
 {
 $dbCreds = $scope->getSuite()->getSetting("contexts")[0]["FeatureContext"];
 
 
 shell_exec("sh /var/www/scripts/db.sh clean {$dbCreds[0]} {$dbCreds[1]} {$dbCreds[2]}");
 } tests/behat/features/bootstrap/FeatureContext.php
  44. Credits • Rob Allen’s (@akrabat) ZF1 tutorial (http://akrabat.com/zend- framework-tutorial/ )

    • Behat step definitions: http://docs.behat.org/en/v3.0/guides/ 2.definitions.html • Behat contexts: http://docs.behat.org/en/v3.0/guides/ 4.contexts.html • Traversing elements: http://mink.behat.org/en/latest/guides/ traversing-pages.html • Behat hooks: http://docs.behat.org/en/v3.0/guides/3.hooks.html