Automated Testing Abhishek Anand Technical Architect & TAM ACQUIA Prachi Nagpal Drupal Developer ACQUIA
TEST DRIVEN DEVELOPMENT 1. Write a test 2. Run the test 3. Let the test fail 4. Write enough code for the test to pass 5. Run your test again 6. Refactor/ clean up the code 7. Run test again 8. Repeat
WHY USE TDD 1. Better understanding of what you're going to write 2. Enforces the policy of writing tests a little better 3. Speeds up development
BENEFITS OF TDD 1. Testable code 2. Clean design 3. Able to be refactored with confidence 4. The minimal code necessary to satisfy the story card 5. A living specification of how the code works 6. Able to support a sustainable pace of new features
TOOLS
WHY USE CODECEPTION 1. Powered by PHPUnit 2. Any type of test a. Acceptance b. Functional c. Unit 3. Easy to extend 4. Lot of test suits already available for Drupal 5. Reusable code 6. Run all types of test from one place
INSTALLATION
INSTALLATION
GETTING STARTED By default AcceptanceTester relies on PhpBrowser module, which is set in tests/acceptance.suite.yml It can also be set to use WebDriver class_name: WebGuyTester modules: enabled: - WebDriver: url: http://drupal812 browser: chrome wait: 2000 - HelperWebGuy
➜ php codecept.phar generate:cept acceptance Signin Test was created in /Users/prachi.nagpal/Sites/drupal8/tests/acceptance/SigninCept.php <?php $I = new AcceptanceTester($scenario); $I->wantTo('Test index page'); $I->amOnPage('/'); $I->see('Site-Install','#header #site-name'); $I->amGoingTo('login in the test app'); $I->fillField('Username','admin'); $I->fillField('Password','1234'); $I->click('Log in'); $I->see('Log out'); $I->click('Log out'); $I->see('User login'); ?>
EXAMPLE <?php $I = new AcceptanceTester($scenario); $I->wantTo('ensure that frontpage works'); $I->amOnPage('/'); $I->see('Home'); ?> <?php $I = new AcceptanceTester($scenario); $I->amOnPage('/'); $I->click('Sign Up'); $I->submitForm('#signup', ['username' => prachi.nagpal, 'email' => 'prachi.nagpal@acquia.com']); $I->see('Thank you for Signing Up!'); ?>
EXAMPLE Codeception can even 'naturalize' this scenario, converting it into plain English: I WANT TO SIGN IN I am on page '/user' I fill field 'username', 'prachi.nagpal' I fill field 'password', '1234' I click 'Log in' I see 'Welcome, prachi.nagpal!'
RUNNING TESTS Tests can be started with the run command. ➜ php codecept.phar run ➜ php codecept.phar run acceptance ➜ php codecept.phar run acceptance SigninCept.php ➜ php codecept.phar run tests/acceptance/SigninCept.php ➜ php codecept.phar run tests/acceptance/SignInCest.php:anonymousLogin ➜ php codecept.phar run tests/acceptance/backend
SELENIUM WEBDRIVER WAIT <?php $I->waitForElement('#agree_button', 30); // secs $I->click('#agree_button'); ?> SESSION SNAPSHOTS function test_login($I) { // if snapshot exists - skipping login if ($I->loadSessionSnapshot('login')) return; // logging in $I->amOnPage('/login'); $I->click('Login'); // saving snapshot $I->saveSessionSnapshot('login'); } // in test: $I = new AcceptanceTester($scenario); test_login($I);
MULTI SESSION TESTING $I = new AcceptanceTester($scenario); $I->wantTo('try multi session'); $I->amOnPage('/messages'); $nick = $I->haveFriend('nick'); $nick->does(function(AcceptanceTester $I) { $I->amOnPage('/messages/new'); $I->fillField('body', 'Hello all!') $I->click('Send'); $I->see('Hello all!', '.message'); }); $I->wait(3); $I->see('Hello all!', '.message');
CLEANING UP / DB SNAPSHOTS → Db module → Load a database dump after each passed test. → SQL dump to be put in /tests/_data directory. → Set the database connection and path to the dump in the global Codeception config. # in codeception.yml: modules: config: Db: dsn: '[set pdo dsn here]' user: '[set user]' password: '[set password]' dump: tests/_data/dump.sql
DEBUGGING ➜ php codecept.phar run --debug <?php codecept_debug($I->grabTextFrom('#name')); ?>
REUSING TEST CODE StepObjects ➜ php codecept.phar generate:stepobject acceptance Admin ➜ php codecept.phar generate:stepobject acceptance Admin Add action to StepObject class (ENTER to exit): loginAsAdmin Add action to StepObject class (ENTER to exit): StepObject was created in /tests/acceptance/_support/Step/Acceptance/Admin.php
Step Object <?php namespace StepAcceptance; class Member extends AcceptanceTester { public function loginAsAdmin() { $I = $this; $I->amOnPage('/admin'); $I->fillField('username', 'admin'); $I->fillField('password', '123456'); $I->click('Login'); } } ?> Actual Test <?php use Step/Acceptance/Admin as AdminTester; $I = new AdminTester($scenario); $I->loginAsAdmin(); ?>
PageObjects $ php codecept.phar generate:pageobject Login Defining PageObjects <?php namespace Page; class Login { public static $URL = '/login'; public static $usernameField = '#mainForm #username'; public static $passwordField = '#mainForm input[name=password]'; public static $loginButton = '#mainForm input[type=submit]'; } ?>
Using PageObjects <?php use PageLogin as LoginPage; $I = new AcceptanceTester($scenario); $I->wantTo('login to site'); $I->amOnPage(LoginPage::$URL); $I->fillField(LoginPage::$usernameField, 'bill evans'); $I->fillField(LoginPage::$passwordField, 'debby'); $I->click(LoginPage::$loginButton); $I->see('Welcome, bill'); ?>
PHPUNIT AND DRUPAL 8 ...to be continued by Abhishek Anand
DEATH BY POWERPOINT!!! THANK YOU!THANK YOU! DEATH BY POWERPOINT!!!

Automation testing with Drupal 8

  • 1.
    Automated Testing Abhishek Anand TechnicalArchitect & TAM ACQUIA Prachi Nagpal Drupal Developer ACQUIA
  • 3.
    TEST DRIVEN DEVELOPMENT 1.Write a test 2. Run the test 3. Let the test fail 4. Write enough code for the test to pass 5. Run your test again 6. Refactor/ clean up the code 7. Run test again 8. Repeat
  • 4.
    WHY USE TDD 1.Better understanding of what you're going to write 2. Enforces the policy of writing tests a little better 3. Speeds up development
  • 5.
    BENEFITS OF TDD 1.Testable code 2. Clean design 3. Able to be refactored with confidence 4. The minimal code necessary to satisfy the story card 5. A living specification of how the code works 6. Able to support a sustainable pace of new features
  • 6.
  • 7.
    WHY USE CODECEPTION 1.Powered by PHPUnit 2. Any type of test a. Acceptance b. Functional c. Unit 3. Easy to extend 4. Lot of test suits already available for Drupal 5. Reusable code 6. Run all types of test from one place
  • 8.
  • 9.
  • 10.
    GETTING STARTED By defaultAcceptanceTester relies on PhpBrowser module, which is set in tests/acceptance.suite.yml It can also be set to use WebDriver class_name: WebGuyTester modules: enabled: - WebDriver: url: http://drupal812 browser: chrome wait: 2000 - HelperWebGuy
  • 11.
    ➜ php codecept.phargenerate:cept acceptance Signin Test was created in /Users/prachi.nagpal/Sites/drupal8/tests/acceptance/SigninCept.php <?php $I = new AcceptanceTester($scenario); $I->wantTo('Test index page'); $I->amOnPage('/'); $I->see('Site-Install','#header #site-name'); $I->amGoingTo('login in the test app'); $I->fillField('Username','admin'); $I->fillField('Password','1234'); $I->click('Log in'); $I->see('Log out'); $I->click('Log out'); $I->see('User login'); ?>
  • 12.
    EXAMPLE <?php $I = newAcceptanceTester($scenario); $I->wantTo('ensure that frontpage works'); $I->amOnPage('/'); $I->see('Home'); ?> <?php $I = new AcceptanceTester($scenario); $I->amOnPage('/'); $I->click('Sign Up'); $I->submitForm('#signup', ['username' => prachi.nagpal, 'email' => 'prachi.nagpal@acquia.com']); $I->see('Thank you for Signing Up!'); ?>
  • 13.
    EXAMPLE Codeception can even'naturalize' this scenario, converting it into plain English: I WANT TO SIGN IN I am on page '/user' I fill field 'username', 'prachi.nagpal' I fill field 'password', '1234' I click 'Log in' I see 'Welcome, prachi.nagpal!'
  • 14.
    RUNNING TESTS Tests canbe started with the run command. ➜ php codecept.phar run ➜ php codecept.phar run acceptance ➜ php codecept.phar run acceptance SigninCept.php ➜ php codecept.phar run tests/acceptance/SigninCept.php ➜ php codecept.phar run tests/acceptance/SignInCest.php:anonymousLogin ➜ php codecept.phar run tests/acceptance/backend
  • 15.
    SELENIUM WEBDRIVER WAIT <?php $I->waitForElement('#agree_button', 30);// secs $I->click('#agree_button'); ?> SESSION SNAPSHOTS function test_login($I) { // if snapshot exists - skipping login if ($I->loadSessionSnapshot('login')) return; // logging in $I->amOnPage('/login'); $I->click('Login'); // saving snapshot $I->saveSessionSnapshot('login'); } // in test: $I = new AcceptanceTester($scenario); test_login($I);
  • 16.
    MULTI SESSION TESTING $I= new AcceptanceTester($scenario); $I->wantTo('try multi session'); $I->amOnPage('/messages'); $nick = $I->haveFriend('nick'); $nick->does(function(AcceptanceTester $I) { $I->amOnPage('/messages/new'); $I->fillField('body', 'Hello all!') $I->click('Send'); $I->see('Hello all!', '.message'); }); $I->wait(3); $I->see('Hello all!', '.message');
  • 17.
    CLEANING UP /DB SNAPSHOTS → Db module → Load a database dump after each passed test. → SQL dump to be put in /tests/_data directory. → Set the database connection and path to the dump in the global Codeception config. # in codeception.yml: modules: config: Db: dsn: '[set pdo dsn here]' user: '[set user]' password: '[set password]' dump: tests/_data/dump.sql
  • 18.
    DEBUGGING ➜ php codecept.pharrun --debug <?php codecept_debug($I->grabTextFrom('#name')); ?>
  • 19.
    REUSING TEST CODE StepObjects ➜php codecept.phar generate:stepobject acceptance Admin ➜ php codecept.phar generate:stepobject acceptance Admin Add action to StepObject class (ENTER to exit): loginAsAdmin Add action to StepObject class (ENTER to exit): StepObject was created in /tests/acceptance/_support/Step/Acceptance/Admin.php
  • 20.
    Step Object <?php namespace StepAcceptance; classMember extends AcceptanceTester { public function loginAsAdmin() { $I = $this; $I->amOnPage('/admin'); $I->fillField('username', 'admin'); $I->fillField('password', '123456'); $I->click('Login'); } } ?> Actual Test <?php use Step/Acceptance/Admin as AdminTester; $I = new AdminTester($scenario); $I->loginAsAdmin(); ?>
  • 21.
    PageObjects $ php codecept.phargenerate:pageobject Login Defining PageObjects <?php namespace Page; class Login { public static $URL = '/login'; public static $usernameField = '#mainForm #username'; public static $passwordField = '#mainForm input[name=password]'; public static $loginButton = '#mainForm input[type=submit]'; } ?>
  • 22.
    Using PageObjects <?php use PageLoginas LoginPage; $I = new AcceptanceTester($scenario); $I->wantTo('login to site'); $I->amOnPage(LoginPage::$URL); $I->fillField(LoginPage::$usernameField, 'bill evans'); $I->fillField(LoginPage::$passwordField, 'debby'); $I->click(LoginPage::$loginButton); $I->see('Welcome, bill'); ?>
  • 23.
    PHPUNIT AND DRUPAL8 ...to be continued by Abhishek Anand
  • 24.
    DEATH BY POWERPOINT!!! THANKYOU!THANK YOU! DEATH BY POWERPOINT!!!