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.




23 comments:

  1. could you make video for it.

    ReplyDelete
    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. Hi,
    I'm receiving an error "Method setup() should be static". How do you avoid this issue?
    Thanks

    ReplyDelete
  7. 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
  8. 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
    Replies
    1. I have changes like ..

      public AndroidDriver getDriver() {
      initDriver();
      return androidDriver;
      }

      But looking for a correct fix.

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

    ReplyDelete
  10. Not even possible for this solution to work as you can't even access non-static methods from a static method. @BeforeClass depends on a static method. Classic one.

    ReplyDelete
  11. Hi,

    The question is: After each test, should I quit the driver and then before each test initialize it again?

    This take quite some time.
    If I'm just closing after each test and launching the app before each test works faster but not if I have test from different classes

    If No need to close driver after every test. How do we achieve it? Can you please share the testng.xml file?



    Awaiting for your response!

    Thanks!!

    ReplyDelete
  12. Hi, I have a query ,
    Why are you using Driver.java Class to access the driver object ?

    Why can't we make driver as proctected member in BaseSetup class and use that Android driver ?

    ReplyDelete
  13. Please change Desired capabilities like this

    private DesiredCapabilities capabilities
    private static AndroidDriver androidDriver = null;

    and also inside the initDriver()
    remove this DesiredCapabilities cap = new DesiredCapabilities();
    and replace with cap = new DesiredCapabilities();

    3rd change in the try block

    androidDriver = new AndroidDriver(new URL(serverUrl), capabilities); - Change capabilities to cap.

    it will work

    ReplyDelete
  14. IIT JEE Coaching in Patna | IIT JEE Institute in Patna | NEET Coaching in Patna | Medical Coaching in Patna | JEE Mains, JEE Advance Coaching in Patna | Europa Classes

    JEE Mains, JEE Advance Coaching in Patna
    Hot offers on Amazon]
    Tips on technology

    Anushka Sen

    ReplyDelete
  15. tipsontechnology

    learn every time new tips on technology

    Hey my audience, in this website we’ll post about many tips on technology. many tips on hacking, education and many entertainment niche. i’ll post somethin Tips on technology
    g special for you, Everyday
    So check out it from here

    ReplyDelete
  16. sale all product in hindi

    Hindisales provide you most productive Offers notification for you. If you Want to buy any products for you. Just check offers on that products in hindisales and check it out from here
    Hot offers on Amazon]

    ReplyDelete
  17. Hi where we need to pass Server IP and Port number

    ReplyDelete