The Real-World Challenges of Medical Device Cybersecurity- Mitigating Vulnera...
Breaking the limits_of_page_objects
1. Breaking the Limits of
Page Objects
Robert Bossek
robert.bossek@qualityminds.de
2. .
Software Architecture
1. Enterprise Architecture
2. Security Architecture
& IAM
3. Application Architecture
Testing Essentials
1. Test Design
2. Test Automation
3. Test Management
4. Test Data
Environments
1. DevOps
2. Oracle
3. Technical Architecture
Agile Testing
1. Test Coaching
2. Construction and
Maintenance of the
Regression Test Suite
3. E2E Test Management
Requirements
Engineering
1. Agile RE
2. RE Essentials
3. Testability
Mobile Testing
1. Mobile Testing Strategy
2. Mobile Test Automation
3. Mobile Experience
3. Content
Management
System
• commercial CMS tool
• customized features
• individual components
• various configurations
• various combinations
• various user flows
• test framework:
4.
5. Basic
Selenium
Test
• Selenium methods are accessed
directly from the test case
• Page elements are addressed
directly in the test case
<?php
class LoginCest
{
public function loginTest(SeleniumWebDriver $I)
{
$I->amOnPage('http://www.payback.mx');
$I->fillField(['css' => 'input#cardnumber'], '5010961962');
$I->fillField(['css' => 'input#pin'], '1234');
$I->click(['css' => 'input[type="submit"]']);
$I->see('Welcome');
}
}
6.
7.
8. Simple
Page
Object
• Page elements shall be
encapsulated in a Page Object
• reusable
• maintainable
<?php
class LoginPage
{
public function getUrl(): string
{
return 'http://www.payback.mx';
}
public function getCardnumberField(): array
{
return ['css' => 'input#cardnumber'];
}
public function getPinField(): array
{
return ['css' => 'input#pin'];
}
public function getLoginButton(): array
{
return ['css' => 'input[type="submit"]'];
}
}
9. Simple
Page
Object
<?php
class LoginCest
{
public function loginTest(SeleniumWebDriver $I)
{
$loginPage = new LoginPage();
$I->amOnPage($loginPage->getUrl());
$I->fillField($loginPage->getCardnumberField(), '5010961962');
$I->fillField($loginPage->getPinField(), '1234');
$I->click($loginPage->getLoginButton());
$I->see('Welcome');
}
public function tryToLoginWithWrongPinTest(SeleniumWebDriver $I)
{
$loginPage = new LoginPage();
$I->amOnPage($loginPage->getUrl());
$I->fillField($loginPage->getCardnumberField(), '5010961962');
$I->fillField($loginPage->getPinField(), ‘4321');
$I->click($loginPage->getLoginButton());
$I->dontSee('Welcome');
}
}
• Page elements shall be
encapsulated in a Page Object
• reusable
• maintainable
• Selenium methods are still
accessed directly from the test
case
10.
11. Simple
Page
Object
<?php
class LoginCest
{
public function loginTest(SeleniumWebDriver $I)
{
$loginPage = new LoginPage();
$I->amOnPage($loginPage->getUrl());
$I->click($loginPage->getEmailToggle());
$I->waitForElementVisible($loginPage->getCardnumberToggle());
$I->fillField($loginPage->getEmailField(), ‘user42@test.it');
$I->fillField($loginPage->getPinField(), '1234');
$I->click($loginPage->getLoginButton());
$I->see('Welcome');
}
...
}
• individual special case handling
directly in test case
• danger of
• code duplication
• differing/unstable handling
• losing business case focus
12.
13.
14. Advanced
Page
Object
<?php
class LoginPage
{
private $webDriver;
public function __construct(SeleniumWebDriver $webDriver)
{
$this->webDriver = $webDriver;
}
public function toggleToEmail()
{
$I = this->webDriver;
$I->click($this->getEmailToggle());
$I->waitForElementNotVisible($this->getEmailToggle());
$I->waitForElementVisible($this->getCardnumberToggle());
}
public function toggleToCardnumber()
{
$I = this->webDriver;
$I->click($this->getCardnumberToggle());
$I->waitForElementNotVisible($this->getCardnumberToggle());
$I->waitForElementVisible($this->getEmailToggle());
}
...
• Selenium methods shall also be
encapsulated in the Page Object
• single source of responsibility
• higher stability
• test driver must be passed
to the Page Object
15. Advanced
Page
Object
...
public function fillCardnumberField(string $cardnumber) { ... }
public function fillEmailField(string $email) { ... }
public function fillPinField(string $pin) { ... }
public function clickLoginButton() { ... }
public function loadPage() { ... }
private function getUrl(): string { ... }
private function getCardnumberField(): array { ... }
private function getEmailField(): array { ... }
private function getPinField(): array { ... }
private function getLoginButton(): array { ... }
private function getCardnumberToggle(): array { ... }
private function getEmailToggle(): array { ... }
}
• Selenium methods shall also be
encapsulated in the Page Object
• single source of responsibility
• higher stability
• test driver must be passed
to the Page Object
• Page elements are hidden
in the Page Object
• prevents inproper usage
• maintainable
16. Advanced
Page
Object
<?php
class LoginCest
{
public function loginTest(SeleniumWebDriver $I)
{
$loginPage = new LoginPage($I);
$loginPage->loadPage();
$loginPage->toggleToEmail();
$loginPage->fillEmailField('user42@test.it');
$loginPage->fillPinField('1234');
$loginPage->clickLoginButton();
$I->see('Welcome');
}
}
• Selenium methods shall also be
encapsulated in the Page Object
• single source of responsibility
• higher stability
• test driver must be passed
to the Page Object
• Page elements are hidden
in the Page Object
• prevents inproper usage
• maintainable
• increases readability
• puts focus on business case
17.
18.
19.
20.
21. Breaking
the
Limits
<?php
class LoginSection
{
private $webDriver;
private $cssContext;
public function __construct(
SeleniumWebDriver $webDriver, string $cssContext
) {
$this->webDriver = $webDriver;
$this->cssContext = $cssContext;
}
public function fillCardnumberField(string $cardnumber) { ... }
public function fillEmailField(string $email) { ... }
public function fillPinField(string $pin ) { ... }
public function toggleToEmail() { ... }
public function toggleToCardnumber() { ... }
public function clickLoginButton() { ... }
...
• create separate representations of
section, so-called Section Object
• add section related methods to the
Section Object
• add section context
22. ...
private function getCardnumberField(): array
{
return ['css' => $this->cssContext . ' input#cardnumber'];
}
private function getEmailField(): array
{
return ['css' => $this->cssContext . ' input#email'];
}
private function getPinField(): array
{
return ['css' => $this->cssContext . ' input#pin'];
}
private function getLoginButton(): array
{
return ['css' => $this->cssContext . ' input[type="submit"]'];
}
}
Breaking
the
Limits
• create separate representations of
section, so-called Section Objects
• add section related methods to the
Section Object
• add section context to
• increase accuracy
• induce generic reusability
23. <?php
class LoginPage
{
private $webDriver;
public function __construct(SeleniumWebDriver $webDriver)
{
$this->webDriver = $webDriver;
}
public function loadPage()
{
$this->webDriver->amOnPage($this->getUrl());
}
private function getUrl(): string
{
return 'http://www.payback.mx';
}
...
Breaking
the
Limits
• create separate representations of
section, so-called Section Objects
• add section related methods to the
Section Object
• add section context to
• increase accuracy
• induce generic reusability
• Page related methods remain
in the Page Object
24. ...
private $headerNavigation;
private $loginSection;
public function getHeaderNavigation(): HeaderNavigation
{
if ($this->headerNavigation === null) {
$this->headerNavigation = new HeaderNavigation(
$this->webDriver, 'body > nav#pb-navbar');
}
return $this->headerNavigation;
}
public function getLoginSection(): LoginSection
{
if ($this->loginSection === null) {
$this->loginSection = new LoginSection(
$this->webDriver, 'body > div.pb-container_first');
}
return $this->headerNavigation;
}
}
Breaking
the
Limits
• create separate representations of
section, so-called Section Objects
• add section related methods to the
Section Object
• add section context to
• increase accuracy
• induce generic reusability
• Page related methods remain
in the Page Object
• Page Objects serve as Container
for Section Objects
25. <?php
class LoginCest
{
public function headerLoginTest(SeleniumWebDriver $I)
{
$loginPage = new LoginPage($I);
$loginPage->loadPage();
$inlineLogin = $loginPage->getLoginSection();
$inlineLogin->fillCardnumberField('1111111111');
$inlineLogin->fillPinField('4321');
$headerNavigation = $loginPage->getHeaderNavigation();
$headerNavigation->expandLoginSection();
$headerLogin = $headerNavigation->getLoginSection();
$headerLogin->fillCardnumberField('5010961962');
$headerLogin->fillPinField('1234');
$headerLogin->clickLoginButton();
$I->see('Welcome');
}
}
Breaking
the
Limits
• create separate representations of
section, so-called Section Objects
• add section related methods to the
Section Object
• add section context
• increase accuracy
• induce generic reusability
• Page related methods remain
in the Page Object
• Page Objects serve as Container
for Section Objects
• targeted and parallel usage of the
same section in one test case
26. Test Case
Page Object
Section Object
Test Driver
CSS Context
1
1
uses
has
uses
*
uses
uses
defines
has
*1
*
*
1
1 1
*
*
1 *
27. Breaking
the
Limits
<?php
class HeaderNavigation
{
private $webDriver;
private $cssContext;
private $loginSection;
public function __construct(
SeleniumWebDriver $webDriver, string $cssContext
) {
$this->webDriver = $webDriver;
$this->cssContext = $cssContext;
}
public function getLoginSection(): LoginSection
{
if ($this->loginSection === null) {
$this->loginSection = new LoginSection(
$this->webDriver,
$this->cssContext . ' div.pb-nav-login-panel‘
);
}
return $this->headerNavigation;
}
...
• …
• Section Objects can be Containers
for other Section Objects, too