BEHAT KICKSTART FOR 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 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 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, 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 with password ISteps should not be able to access admin pages # View user login history So that I can verify my permissions And I visit the login history for Then I should get a "" HTTP response # Create standard users for all tests. Background: Examples: Given set content creation mode as "default" | user | user_target | response | password | And load the background users: | um1 | um11 | 404 | "xxx-xxx" | | user | | ug2 | um21 | 404 | "xxx-xxx" | | uorgmanager1 | | ug1 | um32 | 200 | "xxx-xxx" | | umember1b | | genl | "qam" | 200 | "xxx-xxx" |

@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