One of the main burdens of automated GUI test script maintainability is the amount of maintenance needed when object properties change within the application under test. A very common way of minimizing the time it takes to update your automated test scripts is the use of a central object repository (or object map as it’s also referred to sometimes). A basic object repository can be implemented as a collection of key-value pairs, with the key being a logical name identifying the object and the value containing unique objects properties used to identify the object on a screen.
Selenium Webdriver offers no object repository implementation by default. However, implementing and using a basic object repository is pretty straightforward. In this article, I will show you how to do it and how to lighten the burden of test script maintenance in this way.
Note that all code samples below are written in Java. However, the object repository concept as explained here can be used with your language of choice just as easily.
Creating the object repository
First, we are going to create a basic object repository and fill it with some objects that we will use in our test script. In this article, I am going to model a very basic scenario: go to the Bing search engine, search for a particular search query and determine the number of search results returned by Bing. To execute this scenario, our script needs to manipulate three screen objects:
- The textbox where the search string is typed
- The search button to be clicked in order to submit the search query
- The text field that displays the number of search results
Our object map will a simple .properties text file that we add to our Selenium project:
The key for each object, for example bing.homepage.textbox, is a logical name for the object that we will use in our script. The corresponding value consists of two parts: the attribute type used for uniquely identifying the object on screen and the corresponding attribute value. For example, the aforementioned text box is uniquely identified by its id attribute, which has the value sb_form_q.
Retrieving objects from the object repository
To retrieve objects from our newly created object map, we will define an ObjectMap with a constructor taking a single argument, which is the path to the .properties file:
public class ObjectMap { Properties prop; public ObjectMap (String strPath) { prop = new Properties(); try { FileInputStream fis = new FileInputStream(strPath); prop.load(fis); fis.close(); }catch (IOException e) { System.out.println(e.getMessage()); } }
The class contains a single method getLocator, which returns a By object that is used by the Selenium browser driver object (such as a HtmlUnitDriver or a FirefoxDriver):
public By getLocator(String strElement) throws Exception { // retrieve the specified object from the object list String locator = prop.getProperty(strElement); // extract the locator type and value from the object String locatorType = locator.split(":")[0]; String locatorValue = locator.split(":")[1]; // for testing and debugging purposes System.out.println("Retrieving object of type '" + locatorType + "' and value '" + locatorValue + "' from the object map"); // return a instance of the By class based on the type of the locator // this By can be used by the browser object in the actual test if(locatorType.toLowerCase().equals("id")) return By.id(locatorValue); else if(locatorType.toLowerCase().equals("name")) return By.name(locatorValue); else if((locatorType.toLowerCase().equals("classname")) || (locatorType.toLowerCase().equals("class"))) return By.className(locatorValue); else if((locatorType.toLowerCase().equals("tagname")) || (locatorType.toLowerCase().equals("tag"))) return By.className(locatorValue); else if((locatorType.toLowerCase().equals("linktext")) || (locatorType.toLowerCase().equals("link"))) return By.linkText(locatorValue); else if(locatorType.toLowerCase().equals("partiallinktext")) return By.partialLinkText(locatorValue); else if((locatorType.toLowerCase().equals("cssselector")) || (locatorType.toLowerCase().equals("css"))) return By.cssSelector(locatorValue); else if(locatorType.toLowerCase().equals("xpath")) return By.xpath(locatorValue); else throw new Exception("Unknown locator type '" + locatorType + "'"); }
As you can see, objects can be identified using a number of different properties, including object IDs, CSS selectors and XPath expressions.
Using objects in your test script
Now that we can retrieve objects from our object map, we can use these in our scripts to execute the desired scenario:
public static void main (String args[]) { // Create a new instance of the object map ObjectMap objMap = new ObjectMap("objectmap.properties"); // Start a browser driver and navigate to Google WebDriver driver = new HtmlUnitDriver(); driver.get("http://www.bing.com"); // Execute our test try { // Retrieve search text box from object map and type search query WebElement element = driver.findElement(objMap.getLocator("bing.homepage.textbox")); element.sendKeys("Alfa Romeo"); // Retrieve search button from object map and click it element = driver.findElement(objMap.getLocator("bing.homepage.searchbutton")); element.click(); // Retrieve number of search results using results object from object map element = driver.findElement(objMap.getLocator("bing.resultspage.results")); System.out.println("Search result string: " + element.getText()); // Verify page title Assert.assertEquals(driver.getTitle(), "Alfa Romeo - Bing"); } catch (Exception e) { System.out.println("Error during test execution:\n" + e.toString()); } }
You can see from this code sample that using an object from the object map in your test is as easy as referring to its logical name (i.e., the key in our object map).
Object repository maintenance
With this straightforward mechanism we have been able to vastly reduce the amount of time needed for script maintenance in case object properties change. All it takes is an update of the appropriate entries in the object map and we’re good to go and run our tests again.
Thanks to Selenium Master for explaining this concept clearly for me to apply.
An example Eclipse project using the pattern described above can be downloaded here.
Have any arguments about me submitting this on my twitter?
Sure, go ahead!
Any chance you could post download links to the Eclipse projects you use in these posts?
Hi Phil,
I’ve updated this post with a download link for the Eclipse project. If you’d like me to include similar samples for other posts, please let me know.
Yeah, that would be awesome! By the way, this download appears to be the project from your Page Object Pattern post….
Whoops..
Updated once again, this time using the right project. I’ve moved the other link to the correct post, thank you for noticing!
I’ll add the other samples to the correct posts in the coming week, thanks for the suggestion.
nice information
Thank you Suma.
This is turning out to be really handy. And, the project download is a good teaching tool and time-saver on my end.
I’m wondering if you’ve used LoadableComponent and PageFactory…really helpful classes.
Glad to hear it is turning out to be valuable for you, Phil.
I’ve not used those classes before (though I do know PageFactory) but I’m surely going to look into them a bit more now that you’ve recommended them. Cheers for that.
I used your code. It works fine for id,linkText,class etc. But for cssSelector, It is returning By.Selector. Ideally it should return me By.cssSelector.
Becoz of this, I ma unable to proceed. Please help
Hi Sunanda,
could you please post your code where you want to have the cssSelector and any possible error message you are getting? I am only aware of the cssSelector to be honest, see also http://selenium.googlecode.com/git/docs/api/java/org/openqa/selenium/By.html
Thanks!
Just what I was looking for! Useful information, clear steps and the sample download saves a lot of trouble ๐
Thanks Bas…
You’re welcome Maitri! Glad to be of help. Thanks for your kind words!
Thank you so much it was very useful
You’re welcome Shantha! Glad this article was helpful to you.
Hi,
Thank you for this information…
I need some information about .properties file. is it common for application or it is different for each page.
Thank you
Savita
Hi Savita,
for smaller applications a single file is enough. When your application under test is large and your .properties file grows larger, you might consider modularizing it to keep it readable.
Hope that helps.
Very informative and useful post. Thank you so much for shraing this.
Thank you Saunak!
Thanks a ton…Was very helpful ๐
You’re welcome!
Thanks for this post its really informative!
Thank you Akhil, you’re welcome!
I’m trying to implement this in Ruby using cucumber/selenium any thoughts or anything like this come up before?
Hi Mike,
sorry, but I’m strictly a Java guy when it comes to Selenium..
This post was very helpfull for me to create an object map file.
Thanks a ton:):)
You’re welcome Sudharshan!
Hi Bas
This approach is really nice but I’m wondering if it is useful in Page Object Model and PageFactory pattern or not !!!
Hi Sandeep,
an Object Repository like this should be equally easy to use if you’re using the Page Object Model pattern. However, I think it’s a little less useful in that situation as you already optimize the number of times a reference to an object on a page is made. If you’re using POM and you’re still referencing the same button, link or whatever in many methods, then using an Object Repository might still be handy.
I’d have to look deeper into the combination of a PageFactory and an Object Repository, but as objects in the PageFactory are initialized differently I don’t think the two can be combined as easily..
I have a unique application where there are a lot of controls but they can easily be classified by type (single-select, freetext), so I create the selectors for it dynamically. I just use a keyword to match the element name, and list what of control it should be, and then I have a class that is able to interact with different controls… such as with single selects, it can select from a drop down. Our application has custom dropdowns, etc and the typical webdriver stuff doesn’t work. So I guess I’m saying that this works for certain scenarios, but not if your app would need like thousands of selectors. Then you gotta get creative. This would be a good option for smaller, less complex apps.
Hi Anna,
thanks for sharing your insights. I wholeheartedly agree with what you said. Using an object repository is just one approach to object identification and administration, and one that is more suitable for those applications that do not have a lot of objects to interact with. I can see it getting messy when you have a couple of hundred of objects..
I only recently learned about using the PageFactory approach myself and will probably write something about it in a future post. This is another approach to object identification, and one you might be familiar with already. It makes object identification manageable as well, even though I can see problems for dynamic pages that have a lot of objects in this approach too.
Your solution sounds pretty creative and well suited to your situation as far as I can judge from your explanation. Out of curiosity, what type of selectors do you use? XPath or CSS?
you r a gem man!! well done.
Thanks Prashant!
Hi Bas,
Let’s consider an object repository as a row of books in a library.so each book points to an element in the website we need to be tested.so in future if there happens to be a change in the website suppose an element is added or deleted or gets changed we can manipulate it in the object repository.Is this the idea?
otherwise please explain.
That is exactly the idea! Also, if you have 30 tests referencing the same element and the ID property (or any other property you’re using to identify the object) of that element changes, you only need to update it once, instead of 30 times.
I must say that in practice, the Page Object design pattern in combination with a PageFactory is probably used more often.
PageFactory?
https://code.google.com/p/selenium/wiki/PageFactory
Hi Bas,
I downloaded your script which contains three java pages-SeleniumFramework.java,ObjectMap.java and
SeleniumProperties.java.Also two object Repository-objectmap.paroperties and selenium.properties.But how does this work?which file to run first?The file containing Main() function?
Yes, that’s in SeleniumFramework.java. The other classes are helper classes. I forgot to use appropriate package definitions there I think, my bad..
Hi Bas,
I found a video that contains the Page Object design pattern in combination with a PageFactory that you said here i guess.
Check this video-http://executeautomation.com/blog/page-object-model-in-selenium-part-1/
I think if i follow the pattern shown in the video that is separating objects from tests and write the code plus if i use ExtentReport to generate the report,maybe i can automate a full website right?Is the right approach to build framework?Does this avoid redundancy,duplication and all?
Hi Sherin,
that sounds pretty good! Haven’t seen that video yet but I’ll try and check it out.
In the simple.properties text file,the attribute you used to locate all the bing webelements is id.it can also be done by using name, classname, tagname, linktext, cssselector,xpath etc.so for this to work you wrote the below code-
if(locatorType.toLowerCase().equals(“id”))
return By.id(locatorValue);
else if(locatorType.toLowerCase().equals(“name”))
return By.name(locatorValue);
else if((locatorType.toLowerCase().equals(“classname”)) || (locatorType.toLowerCase().equals(“class”)))
return By.className(locatorValue);
else if((locatorType.toLowerCase().equals(“tagname”)) || (locatorType.toLowerCase().equals(“tag”)))
return By.className(locatorValue);
else if((locatorType.toLowerCase().equals(“linktext”)) || (locatorType.toLowerCase().equals(“link”)))
return By.linkText(locatorValue);
else if(locatorType.toLowerCase().equals(“partiallinktext”))
return By.partialLinkText(locatorValue);
else if((locatorType.toLowerCase().equals(“cssselector”)) || (locatorType.toLowerCase().equals(“css”)))
return By.cssSelector(locatorValue);
else if(locatorType.toLowerCase().equals(“xpath”))
return By.xpath(locatorValue);
else
throw new Exception(“Unknown locator type ‘” + locatorType + “‘”);
}
Is that correct?
Pretty much yes, I just didn’t want to build the whole thing but that’s correct.
Hi, Just want to thank you. Since I am new to the selenium, This article on OR has helped in a lot.
Thanks a lotttt…..
You’re welcome, Alok!
Thanks for providing this, but I believe I’ve run into an issue. I use XPaths to locate my elements, and within some xpaths, 2 consecutive colons are used (i.e. xpath://div[@class=’contactus’]/parent::div).
In these cases, I think the following piece of your code is shortening my xpath:
// extract the locator type and value from the object
String locatorType = locator.split(“:”)[0];
String locatorValue = locator.split(“:”)[1];
Can you please confirm that in my xpath’s scenarios with 2 colons, the above code would end my xpaths at the first of my 2 colons. And if it does, could you provide a workaround?
Thank you
Hi Cyrus,
You’re absolutely right, using a colon as a separator character won’t work when using xpaths that contain semicolons. A simple workaround would be to use another separator character, one that does not occur in xpath or any other locator. Maybe a semicolon would be a good candidate? Don’t forget to change the split() call as well.
Hi, thanks for the quick reply. I did try using unique characters for the split() calls, but they didn’t work (I used ” | ” and ” ^ ” characters, and separated my xpaths with those characters accordingly). However I found a workaround, using 3 colons does work.
So now my modified code looks like:
// extract the locator type and value from the object
String locatorType = locator.split(“:::”)[0];
String locatorValue = locator.split(“:::”)[1];
With my sample xpath looking like this:
xpath://div[@class=โcontactusโ]/parent::div
Can’t seem to edit the reply I just made above, but my sample xpath looks like this (3 colons separating):
xpath::://div[@class=โcontactusโ]/parent::div
Glad to see you found a way to get it working!
Thanks for your post. I need your help in locating the split button in salesforce application. There is a small arrow. I tried but couldn’t succeed.
Hey Madhu,
You could always try and right-click the button in Chrome and choose ‘Inspect’ to see its properties. You can also directly copy an XPath locator from there, although it’s probably an ugly one. Without access to the application I can’t help you further I’m afraid..
Hi Bas,
1. Can you guide me on how to to validate expected and actual value in selenium. Is there a generic method that I can use.
2. Is there a way where I can use your Object Repository file (.properties) to store the expected values for various fields (ex:dropdown, multiselect checkbox) so that it is easy to maintain.
I am trying to validate various fields and its values on a salesforce webpage.
Really appreciate you help. Thanks
Hi Alok,
1. There isn’t. Selenium itself cannot perform any validations as it is just a library to automate browser interactions. For generic assertions JUnit or TestNG are often used (for Java).
2. Sure you can do that. Simply read out the previously stored value and compare it to the actual value using (for example) the assertEquals assertion in JUnit or TestNG.
Thanks a lot…not just for reply to my query….but also for creating this wonderful website and for sharing your knowledge with the rest of the world.
You’re welcome! Take it from me I learn a lot from writing about this stuff myself!
Thanks for sharing your wonderful knowledge.
It is very much useful and advance knowledge.
Hi, Just a suggestion.
We can use Switch case for “public By getLocator(String strElement) ” method. since if else logic will traverse through all the code where as switch case will directly go to the desired Locator type.
Cheers.
public static By get_Locator(String strElement) throws Exception
{
/*Retrieve the strValue from the object repository list */
String strLocator = Object_Repository.objObject_Repository_Properties_File.getProperty(strElement);
/*Splitting String to extract the strLocator type and strValue from the object*/
String strLocator_Type = strLocator.split(“:”)[0]; /*Example: strLocator = id:00N54000000SOrU, then strLocator_Type = id */
String strLocator_Value = strLocator.split(“:”)[1];/*Example: strLocator = id:00N54000000SOrU, then strLocator_Value = 00N54000000SOrU */
/* Printing the for testing and debugging purposes
* System.out.println(“Locator is:” + strElement+ ” LocatorType is:” + strLocator_Type + ” LocatorValue is:” + strLocator_Value);
*/
By by; /* Created an instance of By Class */
/*
* Using switch case to get the ‘by’ strValue depending upon “strLocator_Type” and “strLocator strValue”.
* @return by
*/
switch (strLocator_Type)
{
case “id”:
by = By.id(strLocator_Value);
break;
case “name”:
by = By.name(strLocator_Value);
break;
case “classname”:
by = By.className(strLocator_Value);
break;
case “tagname”:
by = By.tagName(strLocator_Value);
break;
case “linktext”:
by = By.linkText(strLocator_Value);
break;
case “partiallinktext”:
by = By.partialLinkText(strLocator_Value);
break;
case “cssselector”:
by = By.cssSelector(strLocator_Value);
break;
case “xpath”:
by = By.xpath(strLocator_Value);
break;
default:
by = null;
throw new Exception(“—-Unknown strLocator type: ” + strLocator_Type + “——” );
}
return by;
}
Sure you can, great suggestion!
Hi Bas,
Can you please have an article on the below:
1. Comparison between Page Object Model vs Properties Files.
2. Data Driven Framework and Hydrid Driven Framework
Hi Alok,
I’ll put those on the to-write list! It might take a while before I get the time to do some research and see whether I can write a blog post about it but I surely appreciate the suggestions!
Thanks….Bas… ๐
Bas,
I am into Selenium C# and I was trying to implement POM using FindBy annotation. However this seems to be not working.
Below here is the snippet :
[TestClass]
[TestFixture]
public class UnitTest1
{
public IWebDriver wd;
public string urlToNavigate;
[FindsBy(How = How.ClassName, Using = “input”)]
//[FindsBy()]
IList listOfInputBox;
string webDriverPath = ConfigurationManager.AppSettings[“WebDriverPath”];
public UnitTest1() { }
public UnitTest1(IWebDriver driver)
{
this.wd = driver;
PageFactory.InitElements(wd, this);
}
[SetUp]
[TestInitialize]
public void TestSetup()
{
wd = new ChromeDriver(webDriverPath);
urlToNavigate = “http://parabank.parasoft.com/parabank/index.htm”;
}
[Test]
[TestMethod]
public void PageMethodNavigate()
{
wd.Url = urlToNavigate;
}
[TestCleanup]
[TearDown]
public void TearDown()
{
wd.Quit();
}
}
Could you please suggest alternative OR types ?
Hey Sagar,
what exactly isn’t working? What are you trying to achieve / what is your test supposed to do?
Hi,
Can this code can also be used for InternetExplorerDriver?
The class contains a single method getLocator, which returns a By object that is used by the Selenium browser driver object (such as a HtmlUnitDriver or a FirefoxDriver):
Or is there any some other way?
I don’t see why not.
Hi,
I’m just wondering if we can use a variable in the properties file , i mean something like nth-of-type(” + i+ “) as part of selector?
Not directly. Theoretically, you could try using a placeholder string (something like #INDEX#) and replace that at runtime with the actual index.
I don’t like the look of that, but if it works for you..
nice informaticon sir …..
I disagree about the usefullness of the object repository in Selenium WebDriver test automation projects.
In my opinion, using object repositories in Selenium projects is a bad practice.
The object repository concept comes from record-and-playback tools such as QTP.
The object repository is a tree-like structure that is created while recording a test.
It has attributes for each element of the page used in the test.
Selenium does not have any record-and-play feature for test automation (Selenium IDE is not an automation tool).
Using object repositories in Selenium projects (as property or csv or xml files) has many disadvantages such as
1. since each locator is a line in a text file, it is impossible to open the declaration of the locator in the IDE (using F3 or Open Declaration)
2. page object classes with no locator variables break the class encapsulation principle (methods of a class should execute on variables of a class)
3. object repositories do not scale well for big pages; since big pages are bad and should be broken down in small pages, should we not have object repositories for each page widget?
4. object repositories do not work with page factory
Read more about the disadvantages on this medium.com article:
https://medium.com/@alexsiminiuc3/dont-use-object-repositories-with-selenium-webdriver-ad14096528e5
Hey Alex,
I agree. I’ve moved on since this post was published as well (it was published April 2014, 3,5 years ago). I’d never use it today. Just like I wouldn’t use page factory ๐
Thanks, Bas.
I had the feeling that you moved on while reading other posts on your excellent blog.
Object repositories are obsolete and should be avoided.
what would you suggest? Page Factory?
This article is of great help !!! Thanks for sharing.
Thank for the article… awesome explanations.
Hey Bas,
Thanks for such an excellent article.In one of your earlier comments,you mentioned that you moved on from storing objects in properties file.Is there a better way to maintain page objects.Does storing objects in a JSON file makes it easier to understand ?Please advise.
I’d rather just store the locators in the Page Objects themselves. Much easier to maintain.
Ok,Thanks for the response.
Hi Bas,
Your articles are really helpful.
After reading your article and comments above i understood maintaining object repository from properties file with object map is not a good idea implementing using pom & page factory options are the best.
Can you please help me on this scenario like testing the application with multiple countries and different languages.
Since i already implemented my framework with POM and page factory for one country and want to convert this framework to multiple countries and languages.
With out object repository how can we achieve this using pom and page factory.
I’ve never done this but I think I wouldn’t do this in Selenium at all. Best to check the source of the translations rather than how the data from it is shown on screen.
Thanks for the reply Bas
POM is a lot of overhead to maintain as the suite gets larger and once pages start sharing elements and functionalities, which is basically every web application out there. Many times automation testers find themselves maintaining POM instead of actually writing tests. Let’s also admit that most automation testers are not developers, making POM notorious for bad implementations. Partly the limitation of the tester but also alludes to the fact that it has a steep learning curve and not tester friendly – this coming from a Java developer like myself. I’ve worked with it long enough to understand it’s a maintenance chore nobody wants to admit because everyone else is using it. POM with Cucumber is the WORST as step definitions/methods are already independent units on their own. POM is also AUT-specific, which makes it unusable in other projects. Any change of the elements requires a recompile of the .java file.
POM is an inflexible and rigid design pattern that builds technical debt over time and bloats the codebase. I have seen suites with 50+ page objects and it is an absolute nightmare. You will need developers to continue it in the long run, not testers.
UI map/object repo is a good compromise of maintainability, learning curve, and delivery. And this article outlines a good approach to it. At the end of the day, it’s really the completed tests that matter to the stakeholders.
Hey Kat, thank you for your insights. I tend to use and teach Page Objects regularly, but as you say, they do have their downsides as well. It really depends on the situation whether or not it is a useful pattern.
Also sorry for taking so long to approve your comment and reply to it, I blame holiday!
Hey Bas,
No problem! The UI map definitely still has its place as does this fine article you wrote. The UI map can scale from medium-large projects – which is usually enough and there’s a faster ROI to it. Companies like to think they’re “big data” but most of the time, they’re really not haha.
A framework is only as good as its maintainability and if the framework cannot live beyond its creator, it’s as good as dead – as does any software. Hence why there is no one-size-fits-all design pattern. ๐
By the way, the StringUtils from Apache Commons Lang has some really splendid methods for splicing/manipulating Strings like subStringAfter() and subStringBefore().
Thank you for that tip, that might come in handy some day!