Thursday, November 28, 2013

Selenium Page Object Elements



I developed my selenium testing after looking into your page object designed pattern training. It was really helpful. Thanks for posting it 

I have written Page object class for Login page to test UI look & feel for web, iphone & tablet. For each verification I have written a method to return cssValue or text for that element.   

Writing that increases lot method defined in a single class. Is there any way to reduce no of methods declared in a page object class? 

Example: 

    public String getBannerCssValue(String cssValue){ 
        return getCssValue(driver.findElement(banner), cssValue); 
    } 

    public String getSmartPhoneLegendText(){ 
        return getElementText(driver.findElement(smartPhoneLegend)); 
    } 

    public String getSmartPhoneLegendCssValue(String cssValue){ 
        return getCssValue(driver.findElement(smartPhoneLegend), cssValue); 
    } 

    public String getTabletLegendText(){ 
        return getElementText(driver.findElement(tabletLegend)); 
    } 

    public String getTabletLegendCssValue(String cssValue){ 
        return getCssValue(driver.findElement(tabletLegend), cssValue); 
    } 

    public String getButtonTextValue(){ 
        return getAttribute(driver.findElement(login), "value"); 
    } 

    public String getSubmitButtonCssValue(String cssValue){ 
        return getCssValue(driver.findElement(login), cssValue); 
    } 

    public String getForgotPasswordCssValue(String cssValue){ 
        return getCssValue(driver.findElement(forgotYourPassword), cssValue); 
    } 

    public String getTabButtonTextValue(){ 
        return getAttribute(driver.findElement(tabletSubmit), "value"); 
    }





You can have your page objects extend the above generic SystemTestPage class.


package com.systemtest.page;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.CacheLookup;
import org.openqa.selenium.support.FindBy;
import fsg.wrap.systemtest.SystemTestPage;
public class LoginPage extends SystemTestPage {
        
    @FindBy(name = "username")
    @CacheLookup
    private WebElement userNameInput;
  
 @FindBy(name = "password")
    @CacheLookup
    private WebElement passwordInput;
    @FindBy(xpath ="//input[@type=\"submit\"]")
    @CacheLookup
    private WebElement submitButton;
     
    public LoginPage(WebDriver driver){
        super(driver);
    }
     
    public void login(String userName, String password) {
        userNameInput.sendKeys(userName);
  userNameInput.sendKeys(password);
        submitButton.submit();
    }
     
    public String getUserName(String userName) {
        WebElement element = driver.findElement(By.xpath("//td[text()=" + userName + "]"));
        return element.getText();
    }
}


Write the JUnit class using the page objects defined above and assert the results. These JUnit test cases can be run to test your web pages.


package com.unittests;
import junit.framework.Assert;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.support.PageFactory;
import org.openqa.selenium.support.ui.WebDriverWait;
import com.systemtest.page.LoginPage;
public class LoginTest {
     
    private static final String BASE_URL = "http://localhost:7001/login/login.htm";
    private static final String USER_NAME = "John";
 private static final String PASSWORD = "aa44"
     
    private WebDriver driver;
    private WebDriverWait wait;
    private LoginPage loginPage; // the page object
     
    @Before
    public void setUp() {
        driver = new FirefoxDriver();
        wait = new WebDriverWait(driver, 5);
        driver.get(BASE_URL);
        loginPage = PageFactory.initElements(driver, LoginPage.class);
    }
     
    @Test
    public void testLogin() throws Exception {
        loginPage.login(USER_NAME, PASSWORD);
        Assert.assertEquals(MAC, loginPage.getUserName(USER_NAME));
    }
    @After
    public void tearDown() {
        driver.quit();
    }

}

Q. What are selenium locators? What tools do you use to locate them?
A. Selenium Locators are the way of finding the HTML element on the page to perform a Selenium action on. The example above has a line asshown below to extract the username element from the Login response page. This uses an XPath expression to locate the element.

?
1
2
3
4
public String getUserName(String userName) {
    WebElement element = driver.findElement(By.xpath("//td[text()=" + userName + "]"));
    return element.getText();
}

The XPath expression will be something like //td[[text()=John] which looks for a td element with text value "John".

The annotation in the above example is also a locator by name as shown below

?
1
2
3
@FindBy(name = "username")
@CacheLookup
private WebElement userNameInput;  

This will match the HTML snippet

?
1
<input type="text" name="username">

You could also find by tagName, id, css, etc.


There are handy tools to identify the HTML elements or locators.

  • Selenium IDE, which is a Firefox plugin useful in identifying the locators, debugging, etc.


Q. In your experience, what are some of the challenges with Selenium?
A. In general, badly written test cases whether junit tests or web tests, the biggest complaint is about writing test cases that are not maintainable. Unmaintainable automated test cases can take more time than manual tests. So, it is important to write quality test cases by clearly separting the page objects from the test cases as demonstrated  in the Q&A above. The use of the locators need to be carefully thought through. For example, some frameworks like JSF dynamically generate HTML element IDs. So, if IDs are used in your tests, then the test cases may fail if the IDs have changed. The solution to this problem is to use XPath to find the relevant HTML elements. The ClickAndWait action will not work for AJAX calls, and use "waitForElement" instead. 

No comments:

Post a Comment

TestNG - Can i use the 2 different data providers to same @test methods in TestNG?

public Object [][] dp1 () { return new Object [][] { new Object [] { "a" , "b" }, new Obje...