BEHAT KICKSTART FOR DRUPAL DEVELOPERS
Florida DrupalCamp 2016 Orlando, FL - March 5 - 6, 2016 Peter Sawczynec Engineer INTRO TO DRUPAL TESTING ECOSYSTEM Behat SimpleTest PHPUnit JMeter Drupal Extension Mink Selenium 2.0 Phantom.js Mockery Prophecy Codeception Wraith Casper.js Slimer.js Phantom.css Jenkins Travis CI Circle.ci CrossBrowserTesting.com Sauce Labs New Relic
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS INTRO TO DRUPAL/PHP TESTING ECOSYSTEM
Consider using these tools in your Drupal development process: phpStorm drush composer drupal console
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS Behat INTRO TO BDD
Behaviour Driven Development (BDD)
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS INTRO TO BEHAT
Formal Explanation Behat is a tool that makes behavior driven development (BDD) possible. With BDD, you write human-readable stories that describe the behavior of your application. These stories can then be auto-tested against your application.
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS INTRO TO BEHAT
Informal Explanation Behat is about the concept of using a simple coding language (Gherkin) with simple words to describe things that get done (behaviors) on your website that you want to test using an ecosystem of software libraries/tools.
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS Behat in Drupal BEHAT IN DRUPAL
Behat usage in Drupal involves installing a set of software libraries that work quite seamlessly together
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS INSTALL WITH COMPOSER
In Drupal 7 and 8 install and update Behat and all the additional libraries and components you need with Composer
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS STATE OF TESTING IN DRUPAL 8
● Drupal 8 ships with a vendor directory that contains Behat ● Drupal 8 ships with PHPUnit ● SimpleTest module also ships in D8 core, but must be enabled
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS MINK, MINK EXTENSION, DRUPAL EXTENSION...
● Mink, Mink Extension, Drupal Extension, Selenium, Phantom.js ... ● Software libraries like the above are important adjuncts to Behat, these libraries create the bridge to your web pages and to Drupal so you can run tests on your site
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS Behat Ecosystem in Drupal DRUPAL BEHAT ECOSYSTEM Where your Behat 3.0 Behat tests get Mink written Headless Browsers Behat Feature files with steps,
Browser the “Tests” Goutte Controllers
Selenium2 Gherkin Behat Context Phantom.js PHP class files, the testing code
PHP
Website, Drupal Extension
website pages (drupal and drush)
Drupal EXECUTION CODE INTRO TO BDD
The space where as a Drupal developer Where your you write Behat tests and supporting Behat tests get written code logic using Gherkin and PHP
Behat Feature Behat Context files, class files,
the “Tests” the testing code
Gherkin PHP Drupal Writing Behat Testing for Drupal STEPS, SCENARIOS, BACKGROUND
Comments and Annotations ● More useful and required in your code. ● Used by Drupal, Behat, Symfony and other frameworks to “discover” your code and what it does
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS STEPS, SCENARIOS, BACKGROUND
Comments /** * Step function to visit the last created node of a specific type. and * Annotations * @param string $type * The type of node that should be visited. Examples: * * @Given I visit the last created :type node */ Comment /** * Views query plugin for an XML query. * Annotation * @ingroup views_query_plugins * (contains info for * @ViewsQuery( the framework) * id = "views_xml_backend", * title = @Translation("XML Query"), * help = @Translation("Query will be generated and run using the XML backend.") * ) */
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS INTRO TO BDD Feature file # Groups: Event, search (Gherkin) Given I run drush "search-api-index" Then I login And I click "Events" Then I click "New Group Event" Behat Feature Then I should see the text "Looking for members?" Andfiles, I follow the link element with xpath "//a[contains(@href,'/group-ela')]" Giventhe “Tests” I visit the last created "article" node Context class file Gherkin /** * Step function to visit the last created node of a specific type. (PHP) * * @param string $type * The type of node that should be visited. * * @Given I visit the last created :type node Behat Context class files,*/ public function iVisitTheLastCreatedNodeByType($type) { the testing $node code = $this->getLastCreatedEntityFromDb('node', $type); $this->getSession()->visit($this->locatePath('/node/' . $node->nid)); PHP } Drupal INTRO TO BDD Feature file # Groups: Event, search Given I run drush "search-api-index" (Gherkin) Then I login And I click "Events" Then I click "New Group Event" Behat Feature Then I should see the text "Looking for members?" Andfiles, I follow the link element with xpath "//a[contains(@href,'/group-ela')]" Giventhe “Tests” I visit the last created "article" node Context class file
Gherkin /** (PHP) * Step function to visit the last created node of a specific type. * * @param string $type * The type of node that should be visited. * * @Given I visit the last created :type node Behat Context class files,*/ public function iVisitTheLastCreatedNodeByType($type) { the testing $node code = $this->getLastCreatedEntityFromDb('node', $type); $this->getSession()->visit($this->locatePath('/node/' . $node->nid)); PHP } Drupal INTRO TO BDD Feature file (Gherkin) Then I login Then I should see the text "Looking for members?" Given I visit the last created "article" node Then Behat IFeature logout Context class file files, ...the “Tests” /** (PHP) * Step function to visit the last created node of a specific type. Gherkin * * @param string $type * The type of node that should be visited. *
Behat * @Given Context I visit the last created :type node class files, the */testing code public function iVisitTheLastCreatedNodeByType($type) { PHP Drupal $node = $this->getLastCreatedEntityFromDb('node', $type); STEPS Steps in Gherkin When I am on the homepage Then I should get a "200" HTTP response And I should see the button 'Log in' When I go to "/admin" Then I should get a "403" HTTP response Steps And I should see "Access denied"
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS STEPS, TAGS, SCENARIO Steps in use in a Scenario: Tags @smoke @access Scenario: Anonymous User permissions When I am on the homepage Scenario Title Then I should get a "200" HTTP response And I should see the button 'Log in' When I go to "/admin" Then I should get a "403" HTTP response And I should see "Access denied" Steps
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS TAGGING
@api @permissions Feature: Specific user permissions As an authenticated user Tags to identify the 1. I shouldPoint not be able to access One admin pages whole feature file So that I can verify my permissions
2. @smokePoint Two Scenario: Anonymous User permissions When I am on the homepage 3. Then IPoint should get a "200" Three HTTP response Tags by scenario 4. And I Pointshould see the button Four 'Log in' 5. Point Five
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS STEPS, TAGS, SCENARIO, SCENARIO OUTLINE, BACKGROUND
@api @permissions @member @group @access @info Feature: Specific user permissions Scenario Outline: Access user info, login history As an authenticated user Given I am logged in as
@smoke Scenario: Anonymous User permissions When I am on the homepage Then I should get a "200" HTTP response And I should see the button 'Log in'
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS TABLE NODE
@api @permissions Feature: Specific user permissions As an authenticated user 1. I shouldPoint not be able to access One admin pages So that I can verify my permissions
2.# CreatePoint standard users forTwo all tests. Background: Table Node data Given set content creation mode as "default" Essentially how to pass 3. And loadPontPoint the background ThreeThree users: | user | an array to your Behat | uorgmanager1 | method in your context 4. | umember1bPoint | Four
@smoke 5. Scenario:PointFourPoint Anonymous User Five permissions When I am on the homepage Then IFive should get a "200" HTTP response And I should see the button 'Log in'
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS BEHAT FEATURE FILE
@api @permissions Feature: Specific user permissions As an authenticated user 1. I shouldPoint not be able to accessOne admin pages So that I can verify my permissions
2.# CreatePoint standard users forTwo all tests. Background: Feature file Given set content creation mode as "default" A feature is a file filled 3. And loadPoint the background Three users: | user | with Gherkin code: | uorgmanager1 | tags, steps, background, 4. | umember1bPoint | scenario, etc. @smoke File suffix is .feature, e.g: Scenario:FourPoint Anonymous User permissions When I am on the homepage Smoke-access.feature Then I should get a "200" HTTP response And I Fiveshould see the button 'Log in'
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS FEATURES, SUITES, HOOKS, CONTEXTS: SUITES
@api @permissions Feature: Specific user permissions As an authenticated user I should not be able to access admin pages So that I can verify my permissions@api @permissions Feature: Specific user permissions # Create standard users for all tests.As an authenticated user Background: I should not be able to access admin pages Given set@api content @permissions creation mode So thatas "default" I can verify my permissions And loadFeature: the background Specific users:user permissions | user As an | authenticated# userCreate standard users for all tests. Suite | uorgmanager1 I should not | be able Background:to access admin pages | umember1b So that I |can verify my Given permissions set content creation mode as "default" A set of features And load the background users: @smoke # Create standard users | user for all tests. | Scenario: AnonymousBackground: User permissions | uorgmanager1 | intended to run together When I am Given on the set homepage content creation | umember1b mode as "default" | Then I should And get load a "200"the background HTTP response users: for a purpose And I should | usersee the button | 'Log @smoke in' | uorgmanager1 Scenario:| Anonymous User permissions | umember1b | When I am on the homepage Then I should get a "200" HTTP response @smoke And I should see the button 'Log in' Scenario: Anonymous User permissions When I am on the homepage Then I should get a "200" HTTP response And I should see the button 'Log in'
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS FEATURES, SUITES, HOOKS, CONTEXTS: HOOKS
/** There are hooks: * This Behat hook runs after every step. * * @AfterStep before suite 1.*/ Point One after suite public function failScreenshots(AfterStepScope $scope) { $this->saveScreenshot($filename, $this->screenshotDir); print 'Screenshot at: ' . $this->screenshotDir . $filename; before feature 2. } Point Two } } after feature
3./** PontPoint ThreeThree * This Behat hook runs after every @access step. before scenario * * @AfterStep @access after scenario 4.*/ Point Four public function failScreenshots(AfterStepScope $scope) { $this->saveScreenshot($filename, $this->screenshotDir); before step print 'Screenshot at: ' . $this->screenshotDir . $filename; 5. } PointFourPoint Five after step } } transform Tagged hook Five
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS FEATURES, SUITES, HOOKS, CONTEXTS: CONTEXT CLASS FILES
namespace Tests\Drupal\Behat\Bootstrap\Context;
2.use Behat\PointBehat\Context\SnippetAcceptingContext Two, Drupal\DrupalExtension\Context\RawDrupalContext, Typical declarations Behat\Gherkin\Node\TableNode; and setup as PHP 3./** PontPoint ThreeThree * Defines functionality for performing OT (Operational Tests). class file */ class OT extends RawDrupalContext implements SnippetAcceptingContext { 4. Point Four use \Tests\Drupal\Behat\Bootstrap\Helper\All;
private $currentUser; 5. PointFourPoint Five private $backgroundUsers;
/** * InitializesFive context. * * Every scenario gets its own context instance.
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS FEATURES, SUITES, HOOKS, CONTEXTS: CONTEXT CLASS FILES
/** * Initializes context. * 1. * Every scenarioPoint gets its own context One instance. * You can also pass arbitrary arguments to the * context constructor through a behat.yml. */ 2. public functionPoint __construct() { Two $this->group_path = 'or1-gr1pu'; Every method in the $this->og = new Og(); } class file gacn become 3. PontPoint ThreeThree /** a Gherkin step with the * Setting to create content on pre-existing demo content or not. * correct Annotation 4.* @param Pointstring $mode Four * default|custom * E.g. default uses existing organization as base for all content. * custom creates now top level org. 5.* PointFourPoint Five * @When set content creation mode as :mode */ public function setContentCreationMode($mode) { $this->creationModeFive = $mode; }
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS USING JAVASCRIPT
Phantom.js ● The default Goutte driver grabs the page HTML one-time at page load and then works with that dom. ● Phantom.js can execute jQuery and then access new elements in the dom
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS BEHAT YAML FILE (behat.yml)
default: suites: default: contexts: 1. - Tests\Drupal\Behat\Bootstrap\Context\ApiPoint One - Tests\Drupal\Behat\Bootstrap\Context\Base extensions: Behat\MinkExtension: goutte: 2. guzzle_parameters:Point Two selenium2: Top-level general browser: chrome capabilities: config for all behat test 3. version:PontPoint '' ThreeThree files_path: "%paths.base%/media" sessions. Where you Drupal\DrupalExtension: blackbox: ~ can set your drivers, 4. api_driver:Point drupal Four drush: alias: 'local' etc. root: '/var/www/docroot' 5. drupal:PointFourPoint Five drupal_root: '/var/www/docroot' region_map: main uppertabs: "#tabs-0-main_uppertabs" main lowertabs:Five "#tabs-0-main_lowertabs"
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS BEHAT DEBUGGING STEPS
Then I print last response Then I show last response Then I break
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS BEHAT COMMAND PROMPT PARAMS
INFORMATIONAL
behat --version behat -dl behat -dl | grep wait behat -df
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS BEHAT COMMAND PROMPT PARAMS
RUNNING TESTS behat tests/behat/features behat tests/behat/features/checking.feature behat tests/behat/features --tags '~@wip' behat tests/behat/features --tags=@ot behat tests/behat/features --tags=@ot&&~@access
FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS BEHAT DOCS
DOCS
http://blog.lepine.pro/images/2012-03-behat-cheat-sheet- en.pdf
http://docs.behat.org/en/v3.0/
https://wiki.mahara. org/wiki/Testing/Behat_Testing/Characteristics_of_a_good_t est FLORIDA DRUPALCAMP 2016 | BEHAT KICKSTART FOR DRUPAL DEVELOPERS | PETER SAWCZYNEC | PETER.SAWCZYNEC@CIVICACTIONS Open Discussion
CONFERENCE | PRESENTATION TITLE | NAME | @TWITTER | @CIVICACTIONS Thank you.
CONFERENCE | PRESENTATION TITLE | NAME | @TWITTER | @CIVICACTIONS