DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Data Engineering
  3. AI/ML
  4. Design Patterns in Automated Testing

Design Patterns in Automated Testing

Learn how to make your test automation framework better through Page Objects, Facades, and Singletons.

Anton Angelov user avatar by
Anton Angelov
·
Jul. 13, 15 · Opinion
Like (7)
Save
Tweet
Share
76.97K Views

Join the DZone community and get the full member experience.

Join For Free

in my previous articles from the series “ design patterns in automation testing “, i explained in details how to make your test automation framework better through the implementation of page objects , facades , and singletons . when we have to write tests for more complex use case scenarios, it gets usually harder and harder to follow the open close principle , one of the primary principles part of solid . in this part of the series, i am going to use the strategy design pattern to create extendable validators for an e-commerce module.

strategy design pattern

so far in the "design patterns in automation testing" series

1. page object pattern

2. advanced page object pattern

3. facade design pattern

4. singleton design pattern

5. fluent page object pattern

6. ioc container and page objects

7. strategy design pattern

8. advanced strategy design pattern

9. observer design pattern

10. observer design pattern via events and delegates

11. observer design pattern via iobservable and iobserver

definition

in computer programming , the strategy pattern (also known as the policy pattern ) is a software design pattern that enables an algorithm ‘s behavior to be selected at runtime.

  • defines a family of algorithms.
  • encapsulates each algorithm.
  • makes the algorithms interchangeable within that family.
  • the code is easier to maintain as modifying or understanding strategy does not require you to understand the whole main object.

uml class diagram

strategy design pattern uml diagram

strategy design pattern uml diagram

participants

the classes and objects participating in this pattern are:

  • istrategy (iordervalidationstrategy) – defines an interface common to all algorithms. the context class calls the interface to perform the algorithm, identified by the concrete strategy.
  • concretestrategy (salestaxordervalidationstrategy)- implements the algorithm using the strategy interface.
  • context (purchasecontext) – holds a dependency to istrategy . wraps the calls to the concrete strategies, may provide an interface to the strategies to access its data.

strategy design pattern c# code

test’s test case

the primary goal of the below tests is going to be to purchase different items from amazon . also, the prices on the last step of the buying process should be validated- taxes, shipping costs, gift wrapping expenses, etc.

1. navigate to item’s page

amazon items page

amazon items page

2. click proceed to checkout

3. login with an existing client

login existing client amazon

login existing client amazon


4. fill shipping info
fill shipping info amazon


5. choose a shipping speed

6. select a payment method

select payment method amazon


7. validate the order summary
validate order summary amazon


the primary goal of the design of the test related classes is to enable a decoupled and extendable validation of the different prices on the last page of the purchasing process.

the following class structure is going to be used.

strategy design pattern code structure

strategy design pattern code structure

as you can see, the design of the tests uses heavily page object pattern . there is nothing unusual in the different pages, so i’m not going to paste the code of every single page here because it is not relevant to the articles’ theme. probably, the most interesting logic is located in the shippingaddresspage .

public class shippingaddresspage : basepagesingleton<shippingaddresspage, shippingaddresspagemap>
{
    public void clickcontinuebutton()
    {
        this.map.continuebutton.click();
    }

    public void fillshippinginfo(clientpurchaseinfo clientinfo)
    {
        this.map.countrydropdown.selectbytext(clientinfo.country);
        this.map.fullnameinput.sendkeys(clientinfo.fullname);
        this.map.address1input.sendkeys(clientinfo.address1);
        this.map.cityinput.sendkeys(clientinfo.city);
        this.map.zipinput.sendkeys(clientinfo.zip);
        this.map.phoneinput.sendkeys(clientinfo.phone);
        this.map.shiptothisaddress.click();
    }
}


the clientpurchaseinfo class holds the data about the client’s purchase, most of the data is populated through string properties.

public class clientpurchaseinfo
{
    public string fullname { get; set; }

    public string country { get; set; }

    public string address1 { get; set; }

    public string city { get; set; }

    public string phone { get; set; }

    public string zip { get; set; }

    public string email { get; set; }

    public string state { get; set; }

    public string deliverytype { get; set; }

    public giftwrappingstyles giftwrapping { get; set; }
}

implementation without strategy design pattern

the use cases can be automated easily via the usage of facade design pattern .

public class purchasefacade
{
    public void purchaseitemsalestax(string itemurl, string itemprice, string taxamount, clientlogininfo clientlogininfo, clientpurchaseinfo clientpurchaseinfo)
    {
        purchaseiteminternal(itemurl, clientlogininfo, clientpurchaseinfo);
        placeorderpage.instance.validate().estimatedtaxprice(taxamount);
    }

    public void purchaseitemgiftwrapping(string itemurl, string itemprice, string giftwraptax, clientlogininfo clientlogininfo, clientpurchaseinfo clientpurchaseinfo)
    {
        purchaseiteminternal(itemurl, clientlogininfo, clientpurchaseinfo);
        placeorderpage.instance.validate().giftwrapprice(giftwraptax);
    }

    public void purchaseitemshippingtax(string itemurl, string itemprice, string shippingtax, clientlogininfo clientlogininfo, clientpurchaseinfo clientpurchaseinfo)
    {
        purchaseiteminternal(itemurl, clientlogininfo, clientpurchaseinfo);
        placeorderpage.instance.validate().shippingtaxprice(shippingtax);
    }

    private void purchaseiteminternal(string itemurl, clientlogininfo clientlogininfo, clientpurchaseinfo clientpurchaseinfo)
    {
        itempage.instance.navigate(itemurl);
        itempage.instance.clickbuynowbutton();
        previewshoppingcartpage.instance.clickproceedtocheckoutbutton();
        signinpage.instance.login(clientlogininfo.email, clientlogininfo.password);
        shippingaddresspage.instance.fillshippinginfo(clientpurchaseinfo);
        shippingaddresspage.instance.clickcontinuebutton();
        shippingpaymentpage.instance.clickbottomcontinuebutton();
        shippingpaymentpage.instance.clicktopcontinuebutton();
    }
}

the main drawback of such solution is the number of different methods for the different tax verification cases. if there is a need to introduce a new tax validation, a new method should be added that is going to break the open close principle .

strategy design pattern implementation

there are hundreds of test cases that you can automate in this sample e-commerce module. some of the most important are related to the correctness of the prices on the last page of the purchasing process. so the primary goal of the validations in the tests is going to be to test if the correct prices are displayed. there is a couple of types of taxes- sales (us/canada), vat (eu union countries), gift wrapping, shipping, etc. the page objects can be combined in the purchasecontext class to perform a new purchase, the strategy design pattern can be applied to pass the specific validation strategy.

order summary validation strategy



the main method of the validation strategy can be defined by the following interface.

public interface iordervalidationstrategy
{
    void validateordersummary(string itemprice, clientpurchaseinfo clientpurchaseinfo);
}


the purchasecontext class is almost identical to the already discussed facade classes ; the only difference is that it holds a dependency to the to the iordervalidationstrategy .

the concrete validation strategy is passed to its constructor.

public class purchasecontext
{
    private readonly iordervalidationstrategy ordervalidationstrategy;

    public purchasecontext(iordervalidationstrategy ordervalidationstrategy)
    {
        this.ordervalidationstrategy = ordervalidationstrategy;
    }

    public void purchaseitem(string itemurl, string itemprice, clientlogininfo clientlogininfo, clientpurchaseinfo clientpurchaseinfo)
    {
        itempage.instance.navigate(itemurl);
        itempage.instance.clickbuynowbutton();
        previewshoppingcartpage.instance.clickproceedtocheckoutbutton();
        signinpage.instance.login(clientlogininfo.email, clientlogininfo.password);
        shippingaddresspage.instance.fillshippinginfo(clientpurchaseinfo);
        shippingaddresspage.instance.clickcontinuebutton();
        shippingpaymentpage.instance.clickbottomcontinuebutton();
        shippingpaymentpage.instance.clicktopcontinuebutton();
        this.ordervalidationstrategy.validateordersummary(itemprice, clientpurchaseinfo);
    }
}


in most cases, i believe that the best approach to validate taxes and similar money amounts is to call the real production web services that are used to power the actual e-commerce module. they should be already entirely tested via unit and integration tests, and their output should be guaranteed.

strategy design pattern validators

the primary goal of the e2e tests is to ensure that the correct prices are visualized on the page rather than to check the real calculation logic. so in my concrete implementation of the

public class salestaxordervalidationstrategy : iordervalidationstrategy
{
    public salestaxordervalidationstrategy()
    {
        this.salestaxcalculationservice = new salestaxcalculationservice();
    }

    public salestaxcalculationservice salestaxcalculationservice { get; set; }

    public void validateordersummary(string itemsprice, clientpurchaseinfo clientpurchaseinfo)
    {
        states currentstate = (states)enum.parse(typeof(states), clientpurchaseinfo.state);
        decimal currentitemprice = decimal.parse(itemsprice);
        decimal salestax = this.salestaxcalculationservice.calculate(currentitemprice, currentstate, clientpurchaseinfo.zip);

        placeorderpage.instance.validate().estimatedtaxprice(salestax.tostring());
    }
}

i have implemented a similar logic for the vat tax validation strategy.

public class vattaxordervalidationstrategy : iordervalidationstrategy
{
    public vattaxordervalidationstrategy()
    {
        this.vattaxcalculationservice = new vattaxcalculationservice();
    }

    public vattaxcalculationservice vattaxcalculationservice { get; set; }

    public void validateordersummary(string itemsprice, clientpurchaseinfo clientpurchaseinfo)
    {
        countries currentcountry = (countries)enum.parse(typeof(countries), clientpurchaseinfo.country);
        decimal currentitemprice = decimal.parse(itemsprice);
        decimal vattax = this.vattaxcalculationservice.calculate(currentitemprice, currentcountry);

        placeorderpage.instance.validate().estimatedtaxprice(vattax.tostring());
    }
}

one of the essential benefits of the usage of the s trategy design pattern is that if a new tax is introduced in the application, you don’t have to modify your existing pages or the purchasecontext . you will need only to create a new concrete strategy.

for example, if amazon announces that from tomorrow a super model can deliver you the desired items directly to your door, only for 100 bucks. you can handle the new tax validation by creating a new supermodelordervalidationstrategy . imagine brad pitt bringing a refrigerator on his back, i will definitely pay to see this.

change order validation strategy

tests using strategy design pattern

[testclass]
public class amazonpurchase_purchasestrategy_tests
{       
    [testinitialize]
    public void setuptest()
    {
        driver.startbrowser();
    }

    [testcleanup]
    public void teardowntest()
    {
        driver.stopbrowser();
    }

    [testmethod]
    public void purchase_seleniumtestingtoolscookbook()
    {
        string itemurl = "/selenium-testing-cookbook-gundecha-unmesh/dp/1849515743";
        string itemprice = "40.49";
        clientpurchaseinfo clientpurchaseinfo = new clientpurchaseinfo()
        {
            fullname = "john smith",
            country = "united states",
            address1 = "950 avenue of the americas",
            state = "new york",
            city = "new york city",
            zip = "10001-2121",
            phone = "00164644885569",
            giftwrapping = enums.giftwrappingstyles.none
        };
        clientlogininfo clientlogininfo = new clientlogininfo()
        {
            email = "g3984159@trbvm.com",
            password = "asdfg_12345"
        };

        new purchasecontext(new salestaxordervalidationstrategy()).purchaseitem(itemurl, itemprice, clientlogininfo, clientpurchaseinfo);
    }
}

the use of the s trategy design pattern to create automated tests is easy. the only thing that you have to do is to pass the desired algorithm to the newly created purchasecontext .

source code

you can download the full source code from my github repository- https://github.com/angelovstanton/projects/tree/master/patternsinautomation.tests

Software design Testing Object (computer science) Algorithm

Published at DZone with permission of Anton Angelov, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • Testing Level Dynamics: Achieving Confidence From Testing
  • Fargate vs. Lambda: The Battle of the Future
  • The 5 Books You Absolutely Must Read as an Engineering Manager
  • 10 Things to Know When Using SHACL With GraphDB

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: