Reputation: 1338
I have a test suite that currently runs across different development environments. Recently a complete rewrite of the application was done & deployed to a new environment.
The application looks & acts almost identically. Page logic is more or less the same. The big difference is the HTML rewrite has rendered my locators useless. I am unsure how to deal with the locators for this new environment while at the same time adhering to the page object model.
The page object model states that all page logic should be kept in the respective page object class. I am assuming that this includes locators as well.
Following this strategy would leave me with a bloated page object class full of duplicate locators. Is there any recommended best practices or clean solutions to combat this problem ?
The possible solutions i can think of are:
Can anyody comment on whether or not these solutions sound ok ? Or offer any alternative suggestions ?
Upvotes: 0
Views: 1569
Reputation: 11
This is how I managed to overcome this.
I kept different element property files for different environments.
Lets say environment A, B In my project I keep two property files named Elements_A.properties and Elements_B.properties These property files has all the page elements. If one element differs from the other, it will not be a problem since when running the script based on the environment you can refer the relevant property file in the script.
Lets say in A and B in HomePage there is a text box with different locators.
So in property file A we can mention the element as HomePage_Name_TextBox = id_NameInA "id_NameInA" is the locator value and "HomePage_Name_TextBox" is the string you going to use to refer that particular element.
Like wise in property file A we can mention the same element as HomePage_Name_TextBox = id_NameInB "id_NameInB" is the locator value and "HomePage_Name_TextBox" is the string you going to use to refer that particular element.
You can notice that both elements are given the same name (HomePage_Name_TextBox) and the locator vales are different.
In every page class I declared a Map and now you have several options to decide how you going to initialize elements for your page.
public class HomePage {
Map<String, String> elementsMap = new HashMap<String, String>();
//Option 1
public HomePage(Map<String, String> elementMapObj) {
elementsMap = elementMapObj;
}
//Option 2
public HomePage() {
Properties prop = new Properties();
FileReader reader;
HashMap<String, String> propertyMap = new HashMap<String, String>();
try {
reader = new FileReader(new File("CommonConfig.properties"));
prop.load(reader);
for (String key : prop.stringPropertyNames())
{
String value = prop.getProperty(key);
propertyMap.put(key, value);
}
} catch (Exception e) {
//System.out.println(e.toString());
}
try {
reader = new FileReader(new File(propertyMap.get("ElementPropFilePath")));
prop.load(reader);
for (String key : prop.stringPropertyNames())
{
String value = prop.getProperty(key);
elementsMap.put(key, value);
}
} catch (Exception e) {
//System.out.println(e.toString());
}
}
}
When you want to run scripts in A, you can change the "ElementPropFilePath" in the common property file to "Resources/Elements_A.properties" before running. When you want to run scripts in B, you can change the "ElementPropFilePath" in the common property file to "Resources/Elements_B.properties" before running (This is the location of the file in the machine).
Simply to say, if you maintain property files containing all the elements for each environment, and provide that property details and fill the elementMap you have inside each and every page class then you will be able to refer that element with the common string you used for both environment (which is HomePage_Name_TextBox in this example)
Upvotes: 0
Reputation: 9058
Would definitely move the locators outside the pageobject class into two different classes, one for the old locators and one for the new locators. Use public static final String for each locator. The problem you are going to have is that Java annotation values require constant expression, so you cannot use a method to send different locator to the FindBy. But you can use a ternary operator to create a constant expression.
Below I have added code which on the basis of the global flag clicks on a different button from a single WebElement but the locator is changed by an expression on the 'using' value of the FindBy annotation. You can set the value of the global flag on startup when you initialize a driver from a properties file.
This is what you will need to include in the FindBy and locators sent to findElement() - using= GlobalFlag.devEnv ? NewLocators.newLocxpath : OldLocators.newLocxpath. This will be a pain to copy paste everywhere.
You can try out the code as the website is publicly available.
class CartConstant {
//Old locators
public static final String cartxpath = "//span[.='Cart']";
}
class AccountConstant {
//New locators
public static final String accxpath = "//span[.='Account']";
}
class GlobalFlag {
//Initialize this at the start
public static final boolean devEnv = true;
}
public class ChangeAnnotation {
//Change this in the code to include the choice
@FindBy(how=How.XPATH, using=GlobalFlag.devEnv ? AccountConstant.accxpath : CartConstant.cartxpath)
private WebElement butt;
@Test
public void demoSQA() throws InterruptedException {
System.setProperty("webdriver.chrome.driver", "E:/Software Testing/Selenium/Jars/chromedriver.exe");
ChromeOptions chop = new ChromeOptions();
chop.addArguments("test-type");
chop.addArguments("start-maximized");
WebDriver driver = new ChromeDriver(chop);
driver.get("http://store.demoqa.com/products-page/product-category/imacs/");
Thread.sleep(3000);
PageFactory.initElements(driver, this);
butt.click();
}
}
Upvotes: 3