4

Issue:

The target webpage has many buttons in the page and adding separate abstraction for each method like , click button1 , button2 etc feels like unnecessary and just creates additional lines of code.

So to address this the below approaches where suggested:

Approach 1:

Create a map of element and pass the element name as key

Page Object:

package page_objects;

import java.util.HashMap;
import java.util.Map;

import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

import driversetup.TestBaseClass;

public class homepage extends TestBaseClass {

    private Map<String, WebElement> elements = new HashMap<String, WebElement>();
    
    public homepage() {
        driver.get().get("https://courses.ultimateqa.com/users/sign_in");
    }
    
    public void initializeElements() {
        elements.put("password_field", driver.findElement(By.xpath("//input[@type=\"password\"]")));
        elements.put("email_field", driver.findElement(By.xpath("//input[@type=\"email\"]")));
    }

    public homepage inputText(String field, String value) {
        elements.get(field).sendKeys(value);
        return this;
    }

}

Test case:

package test_suites;

import static org.testng.Assert.assertTrue;
import java.io.IOException;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import Utils.excelParcer;
import driversetup.TestBaseClass;
import page_objects.homepage;

public class Testcase1 extends TestBaseClass {

    homepage homepage;

    @BeforeMethod
    public void setUp() {
        initializeTestBase();
        homepage = new homepage();
        homepage.initializeElements();
    }

    @Test(description = "Test case 1 assert True")
    public void loginPageTitleTest2() {
        homepage.inputText("email_field", "test").inputText("password_field", "text2").click(homepage.signin);
    }

    @AfterMethod
    public void tearDown() {
        driver.quit();
    }

}

Approach 2:

Make elements public and pass the element to the method

Page Object:

package page_objects;

import java.util.HashMap;
import java.util.Map;

import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

import driversetup.TestBaseClass;

public class homepage extends TestBaseClass {

    public WebElement email;
    public WebElement password;
    public WebElement signin;
    
    public homepage() {
        driver.get("https://courses.ultimateqa.com/users/sign_in");
    }
    
    public void initializeElements() {
        email= driver.findElement(By.xpath("//input[@type=\"password\"]"));
        password = driver.findElement(By.xpath("//input[@type=\"email\"]"));
        submit= driver.findElement(By.xpath("//input[@type=\"submit\"]"));
    }

    public homepage inputText(WebElement field, String value) {
        field.sendKeys(value);
        return this;
    }

    public homepage click(WebElement field) {
        field.click();
        return this;
    }

}

Test case:

    package test_suites;
    
    import static org.testng.Assert.assertTrue;
    import java.io.IOException;
    import org.testng.annotations.AfterMethod;
    import org.testng.annotations.BeforeMethod;
    import org.testng.annotations.DataProvider;
    import org.testng.annotations.Test;
    import Utils.excelParcer;
    import driversetup.TestBaseClass;
    import page_objects.homepage;
    
    public class Testcase1 extends TestBaseClass {
    
        homepage homepage;
    
        @BeforeMethod
        public void setUp() {
            initializeTestBase();
            homepage = new homepage();
            homepage.initializeElements();
        }
    
        @Test(description = "Test case 1 assert True")
        public void loginPageTitleTest2() {
            homepage.inputText(homepage.email, "test").inputText(homepage.password, "text2").click(homepage.signin);
        }
    
        @AfterMethod
        public void tearDown() {
            driver.quit();
        }
    
    }

Question

Which of the two is better , and if both are not suggested why would making webelement private and create separate methods for each element recommended

2

In the first case, you are coupling your tests with the page object through a convention on the strings "email_field" and "password_field"

If a change is not done correctly, you would discover it only at run time. Not so good.

In the second case, you are coupling through the attributes. Erroneous changes would be catch at compilation time, but, nonetheless, you would have to change every instance of the attributes in the tests and, worse of all, assume that your test would not mess up the meaning of each attribute (because they are public).

What you can do is to create an object for the credentials pass it to a function of your page object, leaving its internals hidden.

The test:

@Test(description = "Test case 1 assert True")
public void loginPageTitleTest2() {
    Credentials credentials = CredentialsFactory().password("text2").email("test").build();
    homepage.signIn(credentials);
}

And the page object:

public Homepage signIn(Credentials credentials) {
    findEmailField().sendKeys(credentials.getEmail());
    findPasswordField().sendKeys(credentials.getPassword());
    findSubmitButton().click();
    return this;
}

Then the findX() functions can be done as you wish. I would suggest searching to the element only at the time, if you don't see necessity of optimizing it at the moment ("early optimization is the root of all evil):

private WebElement findEmailField() {
    return driver.findElement(By.xpath("//input[@type=\"email\"]"));
}

Or you can have a single function for searching elements: findComponent

private WebElement findComponent(String componentName) {
    return driver.findElement(findLocatorFor(component));
}

private By findLocatorFor(String componentName) {
    return components[componentName];
}

// Constructor, where you have the map of element name and its locator
public HomePage() {
    components = Map.ofEntries(
                       entry(EMAIL, By.xpath("//input[@type=\"email\"]")),
                       entry(...));
}

// Some constants to avoid duplicating element name as string
private final String EMAIL = "email"
...

public Homepage signIn(Credentials credentials) {
    findComponent(EMAIL).sendKeys(credentials.getEmail());
    findComponent(PASSWORD).sendKeys(credentials.getPassword());
    findComponent(SUBMIT).click();
    return this;
}

This way you just need to keep in sync that every constant that is used by a public function to locate elements actually have a locator on the components map, but you can be sure that the locators won't be changed outside your object because the components map can be private.

12
  • my question is about findEmailField() , you have method for each field . – PDHide Mar 17 at 15:56
  • The second mentioned method in this question is same as the first method mentioned in question . I was wondering what was the draw back of the two approaches , – PDHide Mar 17 at 15:58
  • i agree to first point that passing string is not ideal – PDHide Mar 17 at 15:59
  • thats why approach two was mentioned, but the appraoch you mentioned requires defining new contant fields thats again an overhead – PDHide Mar 17 at 16:00
  • 1
    (Continuing) The problem with this optimization is that (1) it requires people to remember to call initElements and (2) it requires that the three elements being foundable when initElements is called. The approach of searching at the time of use will not have such assumptions, making your page object more flexible to the behavior of the SUT: Only if you really can't continue with your test it will break, rather than break if one of all those assumptions are not fulfilled. Both approaches are ok, you just have to pick what fits better to your context. Nonetheless, they don't require public attr – João Farias Mar 17 at 16:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.