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

Test automation patterns - not only Page Object...

Test automation patterns - not only Page Object Pawel Maciejewski

Test automation patterns - not only Page Object by Pawel Maciejewski
Slides illustrating my speech at Selenium Camp 2017 at Kiev

Avatar for Paweł Maciejewski

Paweł Maciejewski

February 25, 2017

Other Decks in Technology

Transcript

  1. Pawel Maciejewski + 5y of experience in test automation +

    Web based apps + C# windows native apps + Banking, fintech, online classifieds + Created automation in C#, PHP, Ruby + ISTQB Foundation + ISTQB Advanced Technical Test Analyst + I love cats - fb.com/azor.cat
  2. NUMBERS • 704 test runs triggered daily • 23000 tests

    executed daily • 2543 scenarios • Estimation - some tests are executed on demand
  3. Continuous Integration On the way to CD • We test

    each commit in master • We test each PR • Each test run have own docker-based env
  4. Jenkins • 8 machines (24CPUs, 128gb ram each), 3 executor

    each • Separate pipeline for each country and each suite • Results reported to github
  5. Agenda • Test data generation via API • Test entity

    assertion • Page Object Pattern extended
  6. Trojan /** * @Path /trojan/user/{status} * @POST */ public function

    createUser($status, JsonPayload $json) { //(...) return ApiForward::ok()->withPayload(['user' => $user]); } App logic, entity generation
  7. Sinon /** * @param array $params * @return User */

    public function createUser($params = []) { $trojanPost = $this->createTrojanPost($params, 'users/confirmed'); $user = $this->createUser($trojanPost); return new User($user); } Entity
  8. Data generation - PROS • No maintenance and storage of

    test data • No dependance on database schema • Environment independent • Wider coverage
  9. Test entity assertion - API public function testUsersRequest() { $expectedUser

    = $this->sinon->createUser(); $this->api = new UserProfileApi($this->accessToken); $actualUser = $this->api->getUser($expectedUser->id); PHPUnit::assertEquals($expectedUser, $actualUser); } Entity retrieved from trojan Call to tested API Assertion on entities
  10. TEST ENTITY FACTORY Build entity Same entity Build entity Different

    source class UserFactory { public function createEntityFromJson($json) { // (...) return User($data); } public function createEntityFromArray(array $user) { // (...) return User($data); } }
  11. Test entity builder $email = $this->emailField->getElement($this)->getValue($email); (...) $user = $usersBuilder

    ->withEmail($email) ->withPassword($password) ->withType($status) (...) ->build(); Retrieve entity fields from UI Pass value to entity builder Build entity
  12. Regular way public class LoginPage { public LoginPage typePassword(String password)

    { driver .findElement(By.id( "pass")) .sendKeys(password); return this; } } https://github.com/SeleniumHQ/selenium/wiki/PageObject s
  13. Page elements - page design class LoginPage extends OlxBasePage {

    // page elements as class vars public function __construct() { // init elements in constructor } // perform actions with usage of elements }
  14. Page elements - Class variables class LoginPage extends OlxBasePage {

    /** @var TextField */ private $emailField; /** @var TextField */ private $passwordField; /** @var Button */ private $submitButton; // rest of page } Each element have own type Elements are reusable
  15. Page elements - page Constructor class LoginPage extends OlxBasePage {

    // page elements as class vars public function __construct() { parent::__construct('login'); $this->emailField = new TextField(LocatorType:: ID, 'userEmail'); $this->passwordField = new TextField(LocatorType:: ID, 'userPass'); $this->submitButton = new Button(LocatorType:: ID, 'se_userLogin'); } // perform actions with usage of elements } Clear definition
  16. Page elements - Element usage class LoginPage extends OlxBasePage {

    // page elements as class vars public function __construct() { // init elements in constructor } public function fillLoginPassword( $password) { $this->passwordField->getElement($this)->setValue($password); } }
  17. Page element - Base abstract class BaseElement { /** *

    @var RemoteWebElement */ protected $element; public function getElement($page) { $this->element = $this->$page->find()->elementWithId( $this->locator); } } Abstraction over webdriver native element
  18. Page element - EXAMPLE class TextField extends BaseElement { public

    function setValue($value) { $this->element->sendKeys($value); } public function getValue() { return $this->element->getAttribute( 'value'); } } Abstraction over webdriver native element Webdriver native methods
  19. Page factory - MULTIPLE IMPLEMENTATIONS class NewAdPageFactory { public function

    getDesktopPage() { switch (OlxBaseContext::$currentCountry) { case Country::UKRAINE_RU: return new NewAdPageUA(PageType::DESKTOP); (...) default: return new NewAdPageDefault(PageType::DESKTOP); } } (...) } Each country-specific page
  20. Page factory - MULTIPLE ELEMENTS public function getDesktopPage() { switch

    (OlxBaseContext:: $currentCountry) { (...) case Country::KAZAKHSTAN: $params = array_merge( $params, ['breeding' => new DesktopDropdown(LocatorType:: XPATH, 'targetparam153')]); break; default: $params = array_merge( $params, ['breeding' => new TextField(LocatorType:: ID, 'param185')]); break; } return new NewAdCatsPage( $params); } Different elements
  21. Page factory - ONE INTERFACE /** * @Given /^I open

    posting form$/ */ public function iNavigateToPostingForm() { $adPage = (new NewAdPageFactory())->getDesktopPage(); $adPage->open(); } One call for each country