Friday, 2 June 2017

Page Object Model ( POM ) in Appium

Starting an UI Automation in Appium is not a tough task. You just need to locate elements, perform actions on it.
A small Appium test script maintenance looks easy,but ut with time test suite will grow into multiple test scripts. As you add more and more lines to your code, things become tough to maintain.The chief problem with script maintenance is that if 10 different scripts are using the same mobile element, with any change in that element, you need to change all 10 scripts. This is time consuming and error prone.
A better approach to test scripts maintenance is to create a separate class file which would find mobile elements, fill them or verify them which can be reused in all the scripts using that element. In future, if there is a change in the web element, we need to make the change in just 1 class file and not 10 different test scripts.This approach is called Page Object Model(POM). It helps make the code more readable, maintainable, and reusable.

Video Tutorial -

 

What is POM?


1. Page Object Model is a design pattern.
2. Under this model, for each mobile app screen , there should be corresponding page class.
3. This Page class will find the MobileElements of that mobile screen page and also contains Page methods which perform operations on those MobileElements .
4. Name of these methods should be given as per the task they are performing, i.e., if a loader is waiting for the login page to appear, POM method name can be waitForLoginScreenDisplay().

Advantages of POM


1.Page Object Model defines that operations and test scenario flows in the UI should be separated from verification. This concept makes code cleaner and easier to understand.
2.The Second benefit is the object repository is kept independent of test cases, so we can use the same object repository for a different purpose and with different tools. For example, we can integrate POM with TestNG/JUnit for functional Testing and at the same time with JBehave/Cucumber for acceptance testing.
3.Code becomes reusable and optimized .


How to Use POM?



Core Class for Desired Capabilities related setup -
 This class we can use to write core setup related code which is common for executing each Appium test case.


package com.example.anuja.appiumapplication;

import java.io.File;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Properties;

import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
import org.testng.annotations.AfterClass;
import org.testng.annotations.BeforeClass;

import io.appium.java_client.AppiumDriver;
import io.appium.java_client.android.AndroidDriver;
import io.appium.java_client.remote.MobileCapabilityType;

/**
 * Created by Anuja on 6/2/2017.
 */

public class BaseSetup {

        private DesiredCapabilities capabilities = new DesiredCapabilities();
        private static AndroidDriver androidDriver = null;

        private String appiumPort;
        private String serverIp;

        @BeforeClass
        public void setup(){
            initDriver();
        }

        public AndroidDriver getDriver() {
            return androidDriver;
        }

        private void initDriver(){
            System.out.println("Inside initDriver method");

            DesiredCapabilities cap = new DesiredCapabilities();
            cap.setCapability(MobileCapabilityType.PLATFORM_NAME, "Android");
            cap.setCapability(MobileCapabilityType.DEVICE_NAME, "Android device");
            cap.setCapability(MobileCapabilityType.NEW_COMMAND_TIMEOUT, "4000");
            cap.setCapability(MobileCapabilityType.APP, "c://apks//listviewsample.apk");
            cap.setCapability("noReset", true);
            String serverUrl = "http://" + serverIp + ":" + appiumPort + "/wd/hub";


            try
            {
                System.out.println("Argument to driver object : " + serverUrl);
                androidDriver = new AndroidDriver(new URL(serverUrl), capabilities);

            }
            catch (NullPointerException | MalformedURLException ex) {
                throw new RuntimeException("appium driver could not be initialised for device ");
            }
            System.out.println("Driver in initdriver is : "+androidDriver);

        }

        @AfterClass
        public void tearDown(){
            androidDriver.quit();
        }

    }
Driver class -
This class to instantiate driver object.


import io.appium.java_client.android.AndroidDriver;

/**
 * Created by Anuja on 6/2/2017.
 */

public class Driver extends BaseSetup{

    protected AndroidDriver driver;

    public Driver() {
        this.driver = super.getDriver();
    }
}
Page Class for Login Page -
This is how our page class looks.

import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.PageFactory;
import org.testng.Assert;




/**
 * Created by Anuja on 5/17/2017.
 */

public class LoginPage extends Driver{

    PageObjects loginPage;

    String userName = "";
    String passWord = "";

    public LoginPage() {
        super();
        loginPage = new PageObjects();
        PageFactory.initElements(driver, loginPage);
    }

    public boolean validateLoginpage(){
        boolean elements = false;
        if(loginPage.userNameFld.isDisplayed()){
            if(loginPage.passwordField.isDisplayed()){
                if(loginPage.checkBox.isDisplayed()){
                    if(loginPage.loginBtn.isDisplayed()){
                        elements = true;
                    }
                }
            }
        }
        else{
            elements = false;
        }
        return elements;


    }

    public boolean testLoginWithoutCredentials() {
        boolean loginStatus = false;
        loginPage.loginBtn.click();
        if (loginPage.inputError.getText().equalsIgnoreCase("Username is mandatory")) {
            loginStatus = true;
        }
        loginPage.userNameFld.sendKeys(userName);
        loginPage.loginBtn.click();
        if (loginPage.inputError.getText().equalsIgnoreCase("Password is mandatory")) {
            loginStatus = true;
        }
        return loginStatus;

    }



    class PageObjects {

        @CacheLookup
        @FindBy(id = "et_username")
        public WebElement userNameFld;

        @CacheLookup
        @FindBy(id = "et_password")
        public WebElement passwordField;

        @CacheLookup
        @FindBy(id = "btnSignin")
        public WebElement loginBtn;

        @CacheLookup
        @FindBy(name = "Invalid ID or password.")
        public WebElement inputError;

        @CacheLookup
        @FindBy(id = "checkBox")
        public WebElement checkBox;


    }
}

Test Case class for writing login page test cases - 
This is how our test case is going to look.

import org.junit.Test;

/**
 * Created by Anuja on 5/17/2017.
 */

public class LoginTests {

    @Test
    public void testLogin()
    {
        LoginPage loginPage = new LoginPage();
        if(loginPage.validateLoginpage() == true){
            loginPage.testLoginWithoutCredentials();
            System.out.println("pass");
        }
        else{
            System.out.println("Validation failed");
        }

    }
}
I hope you like this post. do share your queries and feedback in comment section below and please follow me on social media for latest post updates.




12 comments:

  1. Replies
    1. yes its in my task .. List soon it will be up on youtube channel.
      Do subscribe to get notifications :-)

      Delete
  2. Hi... Can you please tell me how to run the above example

    ReplyDelete
  3. HI Can you make a video on running the example

    ReplyDelete
  4. can you please send a link for the listviewsample.apk

    ReplyDelete
  5. More interesting articles here :Generation Enggelmundus Internet Marketing Tool here :Zeageat IM

    ReplyDelete

  6. I am curious to find out what blog system you happen to be using? I'm experiencing some small security issues with my latest site and I'd like to find something more safe. Do you have any recommendations? gmail.com login

    ReplyDelete
  7. Hi,
    I'm receiving an error "Method setup() should be static". How do you avoid this issue?
    Thanks

    ReplyDelete
  8. Thank you so much for sharing this worth able content with us. The concept taken here will be useful for my future programs and i will surely implement them in my study. Keep blogging article like this.

    selenium training in bangalore|

    ReplyDelete
  9. Hi Anuja,
    I tried your solution but getnull pointer exception because of the driver doesn't initialized at all.
    while debugging, the "Driver" class get into the getDriver method from "Base" class
    but it's never get into the setup method to call the initDriver for driver initialized so how the driver will be initialized?

    Thanks!

    ReplyDelete
  10. I am using @Androidfindby but the test won't initialize the pages in intellijea. Any reason for this ?

    ReplyDelete