0% found this document useful (0 votes)
16 views12 pages

Design Patterns in Test Automation Uh Hih

Ugupuuuuvvibib

Uploaded by

kamalvignesh95
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
16 views12 pages

Design Patterns in Test Automation Uh Hih

Ugupuuuuvvibib

Uploaded by

kamalvignesh95
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 12

Design Patterns in Test Automation

8
7
This article highlights the design patterns & architectural considerations to take
6
into account when building a Selenium-based / Appium-based Test Automation
5
Framework. The examples are purposely shown in various programming
4
languages to highlight that this thought process is applicable for any
3
programming language you use for building an Automation Framework.
2
Table of Contents – Design Patterns in Test Automation
1
. Selenium WebDriver 101
. Why do we need a Test Automation Framework?
. Criteria for building a Test Automation Framework
. Design Patterns in Test Automation Framework
. Characteristics of a good Page-Object Pattern implementation
. Challenges of using Page-Object Pattern
. Solution: Business-Layer Page-Object Pattern
. Test Automation Framework Architecture

Selenium WebDriver 101


Let’s start by quickly understanding what WebDriver is.
Selenium is one of the most popular web-automation tools. Surprisingly, a lot of
people actually get confused about what Selenium really is.
Here is the official version of what Selenium is: https://www.selenium.dev/
documentation/en/
WebDriver is the core of Selenium. It is an interface that uses browser
automation APIs provided by browser vendors to simulate user behaviour and
interaction by controlling the browser in the form of running tests.
WebDriver drives a browser either locally or on a remote machine using the
Selenium server. Selenium WebDriver refers to both the language bindings and
the implementations of the individual browser controlling code. This is
commonly referred to as just WebDriver.
The Selenium Browser Automation Project also lists a quick example of how to
quickly get started with using WebDriver.
Below is another example of how this code can look like:

var webdriver = require('selenium-webdriver'),


test = require('selenium-webdriver/testing'),
assert = require('assert');

test.describe("Google Search", function() {

test.it("should take user input correctly", function() {

var driver = new webdriver.Builder().


withCapabilities(webdriver.Capabilities.chrome()).build();
driver.manage().timeouts().implicitlyWait(1000);

driver.get("http://localhost:8082/community-app");
var element = driver.findElement(webdriver.By.id("uid"));
element.sendKeys("mifos");
var value = element.getAttribute("value");
value.then(function(value) {
assert.equal(value, "mifos");
});

});

});

Why do we need a Test Automation Framework?


As seen in the above example, it is easy to get started using WebDriver to
simulate user behavior.
However, typically, one uses WebDriver to automate many different user
scenarios / interactions in order to get quick validation if the functionality in the
product-under-test is working correctly, as expected. Some of these products
may have a small-life, however, most of these would be around for a long time.
As a result, the team testing this product would have a lot of user scenarios
that they would want to be automated. I have seen cases where the team has
1000s of tests identified and automated for their product-under-test.
In such cases, continuing to implement tests in similar ways as shown in the
simple, getting-started example will quickly turn into a nightmare for various
reasons of code quality, maintainability, extensibility and scalability.
You may set up some structure for your tests, and build some sort of a
framework, but that will still be limiting as you scale.
<img class="alignnone wp-image-12711 size-full" title="Test implementation in
Ruby" src="https://blog.testproject.io/wp-content/uploads/2020/05/
SomeStructure.png" alt="Test implementation in Ruby" width="379"
height="88" />
In the above example, of test implementation in Ruby, the home_page.rb file
ended up being 1000+ line long, which made it very difficult to quickly
understand what this class ended up doing, and what functionality was
implemented here.
The architecture of such a framework looks as simple as this, which could also
be referred to as non-existent:

We need a better, and structured approach to implementing the test


scenarios!

Criteria for building a Test Automation Framework


Writing code is easy, but writing good code is not as easy. Here are the reasons
why I say this:
● “Good” is subjective.
● “Good” depends on the context & overall objective.
Similarly, implementing automated test cases is easy (as seen from the getting
started example shared earlier). However, scaling this up to be able to
implement and run a huge number of tests quickly and efficiently, against an
evolving product is not easy!
I refer to a few principles when building a Test Automation Framework. They
are:
● Based on the context & (current + upcoming) functionality of your

product-under-test, define the overall objective of Automation Testing.


● Based on the objective defined above, determine the criteria and
requirements from your Test Automation Framework. Refer to my post
on “Test Automation in the World of AI & ML” for details on various
aspects you need to consider to build a robust Test Automation
Framework. Also, you might find these articles interesting to learn how
to select the best tool for your requirements:
○ Criteria for Selecting the Right Functional Testing Tools
○ How to Select the Best Tool – Research Process
○ How To Select The Right Test Automation Tool

Design Patterns in Test Automation Framework


Test Automation (using tools like Selenium / Appium) are essentially
development activities. One needs to have good programming knowledge to be
able to implement tests that are robust, stable, efficient, fast, maintainable and
extensible!
There are many Design Patterns available for Software Development (as seen
here). Since Test Automation is also a Development activity – The
understanding of Design Patterns is critical in Test Automation as well.
Below are some commonly used Design Patterns one would use, directly or
indirectly, in building a Test Automation Framework:
● Builder pattern – https://en.wikipedia.org/wiki/Builder_pattern
● Factory method pattern – https://en.wikipedia.org/wiki/
Factory_method_pattern
● Lazy initialization pattern – https://en.wikipedia.org/wiki/
Lazy_initialization
● Singleton pattern – https://en.wikipedia.org/wiki/Singleton_pattern
● Adapter pattern – https://en.wikipedia.org/wiki/Adapter_pattern
● Composite pattern – https://en.wikipedia.org/wiki/Composite_pattern
● Decorator pattern – https://en.wikipedia.org/wiki/Decorator_pattern
● Façade pattern – https://en.wikipedia.org/wiki/Facade_pattern
● Iterator pattern – https://en.wikipedia.org/wiki/Iterator_pattern
● Servant pattern – https://en.wikipedia.org/wiki/
Servant_(design_pattern)
● Template method pattern – https://en.wikipedia.org/wiki/
Template_method_pattern
● Page-Object Model / Page-Object Pattern – When it comes to
building a Testing Framework, there is a pattern that is most
commonly used – Page-Object Model / Page-Object Pattern –
https://www.selenium.dev/documentation/en/
guidelines_and_recommendations/page_object_models/. This pattern
helps keep the interaction with the product-under-tests logical and
easy to implement, maintain and evolve along with the product
functionality.
● Business-Layer Page-Object pattern – This pattern overcomes the
limitations with the Page-Object Pattern with respect to scaling your

Automated Tests: https://www.slideshare.net/abagmar/perils-of-


pageobject-pattern
Characteristics of a good Page-Object Pattern Implementation
A good page-object pattern implementation would have the following
characteristics:
● Pages are well identified.
● Use Composite Pattern to compose complex pages and also allow
reuse of snippets of pages across the product.
○ Ex: If Search functionality is common across majority of the pages

in your product, then:


◆ Define a SearchPage object.
◆ In other pages, ex: HomePage, LoginPage, ProductPage, etc,

use the Composition Pattern to include SearchPage as part of


its definition.
◆ Example:

◆ <img class="alignnone wp-image-12715 size-full"


title="Composite Pattern" src="https://blog.testproject.io/wp-
content/uploads/2020/05/GoodPageObjects.png"
alt="Composite Pattern" width="390" height="403" />
● Pages do not have any assertions in them.
● Pages do not have getters-setters in the pages (or in any class in your
code). This is a bad practice and it breaks Encapsulation.
● Pages should have logical methods that do action on the page, or gets
information from the page.
● Pages should have no business logic in the Page-Object method
implementation, except handling cases like:
○ If ex: there are Country & State dropdown controls on the page,

and the State values get populated based on which Country is


selected, then the method selecting the Country should wait for
the items in the State dropdown to get populated.
● Each Page object should have an explicit return type, i.e. the Page
object method SHOULD NOT return void.
○ Ex: On successful login, the method should return the HomePage

object.
○ On unsuccessful login, the method should return the same

LoginPage object.
○ There would be cases when you are getting information from the

page itself. In such cases, the methods should return specific


datatypes.
◆ Ex: isLoggedIn should return boolean.
◆ Ex: getLoggedInUserName should return String.
◆ Ex: getRegisteredAddressInProfile should return a custom

object Address, or any other data structure that makes sense


in the context of the product-under-test.
The architecture of such a framework evolves as below:

Challenges of using Page-Object Pattern


Page Objects help implement tests easily, and keep the code well structured.
This allows easy updates in the Page Objects based on evolving product
functionality.
However, there is a challenge.
In the Test implementation methods, ex: if you are using JUnit / TestNG as the
test runner, then in the @Test methods, you now need to orchestrate the Page
object interactions and also add your assertions.
Below is an example of 2 tests:
Test 1:

@Test
public void shouldRevertToOriginalContentAfterClickingCancel() {

PatientInformation patientInfomation = new PatientInformation(firstName,


middleName, lastName, gender);
RegistrationPage registrationPage = new RegistrationPage();
PatientChartPage patientChartPage =
registrationPage.registerPatientWithBasicInformation(patientInfomation);

String originalFirstName = patientChartPage.getFirstName();


assertThat(originalFirstName, is(firstName));

patientChartPage.updateName(newFirstName, newLastName);
patientChartPage.updateAddressLine(newLine1, newLine2);
patientChartPage.updateCity(newCity);
patientChartPage.updateState(newState);
patientChartPage.updateZip(newZip);
patientChartPage.cancelUpdate();

String updatedFirstName = patientChartPage.getFirstName();


assertThat(updatedFirstName, is(originalFirstName));
}
Test 2

@Test
public void
shouldUpdateExistingPatientWithNewInformationAfterClickingSave() throws
InterruptedException {

PatientInformation expectedPatientInformation = new


PatientInformation(firstName, middleName, lastName, gender);
RegistrationPage registrationPage = new RegistrationPage();

registrationPage.registerPatientWithBasicInformation(expectedPatientInformati
on);

String originalFirstName = patientChartPage.getFirstName();


assertThat(originalFirstName, is(firstName));

patientChartPage.updateName(newFirstName, newLastName);
patientChartPage.updateAddressLine(newLine1, newLine2);
patientChartPage.updateCity(newCity);
patientChartPage.updateState(newState);
patientChartPage.updateZip(newZip);
patientChartPage.saveUpdate();

Driver.get().navigate().refresh();

assertThat(patientChartPage.getFirstName(), is(newFirstName));
assertThat(patientChartPage.getLastName()), is(newLastName));
assertThat(patientChartPage.getCity(), is(newCity));
assertThat(patientChartPage.getState(), is(newState));
assertThat(patientChartPage.getAddressLine1()), is(newLine1));
assertThat(patientChartPage.getAddressLine2()), is(newLine2));
assertThat(patientChartPage.getZip()), is(newZip));
}

Here are the challenges of this implementation:


● The intent of the test is lost in the details of the interaction of page
objects and assertions.
● The @Test methods become quite unreadable.
● There is a lot of duplication in both the test implementations.
As you implement more variants of test scenarios, you will encounter there
would be many instances of test implementations needing slight tweaks to the
implemented test to verify variants of the scenarios. In such cases, you would
end up copying most of the implementation from 1 test into the others – which
is wasted effort, code duplication and makes it difficult to update the tests as
the product functionality evolves.
In some cases, you may be able to extract common methods in the same class
to reuse the implementation, but that seems like an ad-hoc way of
implementation.
That is the reason I do not like to use the Page Object Pattern directly in my
Test implementation for larger implementations.

Solution: Business-Layer Page-Object Pattern


It is very important to have the Test specification be crisp and clear. This way
the intent of the test is understood very easily and the reader knows exactly
what is expected from the product-under-test as a result of running that
particular test.
This is the area the Business-Layer Page-Object Pattern helps. Below is how
the implementation flow is using this pattern:
. Tests should talk business language only! No granular operations / UI
interactions.
. The Test implementation is an orchestration of corresponding
business operations as it is simulating a deterministic, specific
scenario.
. Business operations are implemented in a layer between the tests &
.
page objects – called BusinessLayer. This allows for better reuse of
implementation.
. Implementation of an business operation is an orchestration between
other business operations, and / or an orchestration of page objects.
. The business operation does the assertions of expectations, NOT
page-objects.
. Each business operation / page object has a valid return type (never
void). Each operation (in business or page object) being successful
means there are a defined number of methods / operations the
product can now do (as you are driving the product under test to do
your bidding).
Let’s look at some examples of how this can be implemented.
Test Automation Framework Architecture
The overall architecture of the Test Automation Framework that allows me to
implement, evolve and scale in an easy fashion is shown below:

<img class="alignnone wp-image-12712 size-full" title="Business-Layer Page-


Object Pattern" src="https://blog.testproject.io/wp-content/uploads/2020/05/
BusinessLayer-PageObjectPattern.jpeg" alt="Business-Layer Page-Object
Pattern" width="1600" height="900" srcset="https://blog.testproject.io/wp-
content/uploads/2020/05/BusinessLayer-PageObjectPattern.jpeg 1600w,
https://blog.testproject.io/wp-content/uploads/2020/05/BusinessLayer-
PageObjectPattern-512x288.jpeg 512w, https://blog.testproject.io/wp-content/
uploads/2020/05/BusinessLayer-PageObjectPattern-768x432.jpeg 768w,
https://blog.testproject.io/wp-content/uploads/2020/05/BusinessLayer-
PageObjectPattern-1024x576.jpeg 1024w" sizes="(max-width: 1600px) 100vw,
1600px" />
Below is my folder structure for my Test Automation Framework:
@Test(groups = {"a-addcash", "a-demo2", "a-RcRegression"})
public void RepeatDepositAddCashTest() {
log.info("Running RepeatDepositAddCashTest");
PlayerUtil playerUtil = new PlayerUtil(getTestExecutionContext());
HashMap<RegistrationDetails, String> userData =
playerUtil.getUserWithModValue(7, 4);
String userName = userData.get(RegistrationDetails.USERNAME);
String password = userData.get(RegistrationDetails.PASSWORD);
playerUtil.saveAddressDetailsOfUser(userName, password);
new LoginBL().loginUsing(userName, password)
.startAddcash()
.addCashUsingNetBanking(100, "Allahabad", "test", "test")
.verifyOrderID()
.verifyModeOfPayment("NETBANKING")
.verifyGamesLobbyLink()
.done();
}
Here are some sample Business Layer methods:

public FeedbackOverlayBL verifyLoveThisAppThankYouPage() {


return new FeedbackOverlayBL()
.verifyLoveThisAppCTA()
.verifyThankYouPageInLoveThisApp();
}

public LobbyBL goToFeedBackFormAndCancel() {


return new FeedbackOverlayBL()
.goToFeedbackForm().cancelFeedbackForm();
}

public AddCashLimitsBL addAmountForAddcashLimits(int amount) {


ChooseAmountPage.getInstance()
.addAmount(amount);
return new AddCashLimitsBL();
}

public LobbyBL goToCashGames() {


LobbyPage.getInstance().clickCashGamesButton();
return this;
}

Here are some sample Page-Object methods:

public PaymentModePage addAmount(int amount) {


WebElement addCashEnterAmountElement =
driver.findElementByAccessibilityId(addCashEnterAmount);
waitForElementToBePresent(addCashEnterAmountElement);
addCashEnterAmountElement.clear();
addCashEnterAmountElement.sendKeys(String.valueOf(amount));
hideKeyBoard();
eyes.checkWindow("AddAmount");
driver.findElementByAccessibilityId(addCash).click();
return PaymentModePage.getInstance();
}

public boolean isPromoCodeApplySuccessful(String promoCode) {


log.info("Verifying promocode is successfully applied with name: " +
promoCode);

waitForElementToBePresent(driver.findElementByAccessibilityId(appliedPromoc
odeNameId));
eyes.checkWindow("isPromoCodeApplySuccessful");
return
driver.findElementByAccessibilityId(appliedPromocodeNameId).getText().equals
IgnoreCase(promoCode);
}

References:
● Sample github repository: https://github.com/anandbagmar/cuke-jvm-
sample
● Perils of Page-Object Pattern: https://www.slideshare.net/abagmar/
perils-of-pageobject-pattern
● Criteria for building a good Test Automation Framework: Test
Automation in the World of AI & ML (https://www.infoq.com/articles/
test-automation-ai-ml/)

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy