A reusable objectless automation framework for Rational Functional Tester

Reduce development time, minimize maintenance, run test scripts faster, and reuse it for creating test scripts

In this detailed analysis, the author explains design of an automation framework that is independent of the objects in the application under test. The framework is centered on the concept of creating objects dynamically at runtime, rather than recording and storing them in object maps. By using the design advocated in this article, you can develop a reusable automation test framework that can be imported into any Rational Functional Tester development environment. As a bonus, it also drastically reduces the development and execution time of test scripts.

Varun Ojha (varun.ojha@in.ibm.com), Systems Software Engineer, IBM

VarunOjha photoVarun Ojha is a developer for IBM WebSphere adapters and works out of the IBM India Software Labs, in Bangalore. He has five years of experience in working with Java technologies and developing automation frameworks.



10 January 2012

Also available in Chinese

This article describes an approach to automation test script development that does away with the traditional ways of object capture, script recording, and playback. This automation framework can be imported by any test team using IBM® Rational® Functional Tester and can be used to start writing test scripts immediately without any object capture or recording.

How objects are recorded, stored, and maintained in Rational Functional Tester

There are two ways to capture objects in Rational Functional Tester:

  1. The first is through simple recording. As you interact with the application under test (AUT), all of your actions are captured by the tool, along with all of the objects that you interact with.
  2. The second is by using the Insert Object tool to highlight any object in the application and capture its properties. The fundamental data type for Rational Functional Tester is an object. This object has recognition and administrative properties that are captured by the tool from the AUT. These objects are stored in object maps that can be either shared or private. On playback of a script, when the tool encounters an object, such as text box, table, or list, it compares the recognition properties of this object with the objects in the AUT to find a match. If the recognition succeeds, the appropriate action is performed on the object; otherwise, the script fails.

Design of a traditional test script

The following example describes the creation and execution of a simple test script using the traditional approach. The aim of this test script is to verify whether a specific book is displayed in the Books section of Google under the Computers category.

These are the steps involved in completing this test:

  1. Start the browser with www.google.com as the URL.
Figure 1. Google home page
Google screen
  1. Enter the search string in the Google Search bar for books.
  2. Click the More link on the Google home page.
Figure 2. Books link
Screen capture of the link
  1. Under More, click the Books link.
  2. On the Books page, click the Computers category link on the left.
Figure 3. Computers category link
Computers Link
  1. Verify whether the required book is displayed.
Figure 4. Required book
Required Book
  1. Navigate back to the Computers book category page.

To prepare test scripts, the test team generally first creates an AppObjects folder that consists of private object maps representing each page of the AUT. This private object map holds all of the objects on that page. The test team captures the objects by using one of the approaches described earlier. After the object has been captured, the test developer needs to update the recognition properties of all of these objects. Properties such as URL, title, ID, and so forth change from page to page. Therefore, the object recognition might fail unless these properties are changed to regular expressions.

Now imagine a standard enterprise application that consists of hundreds of objects. Capturing each one of them and updating their properties is a very tedious and time consuming task that takes away from the actual functional implementation of the test script.

Listing 1 shows the code for the test script developed using the standard approach

Listing 1. Regular Rational Functional Tester code for the test script
//Open the browser
startBrowser("www.google.com"); //Wait for Google homepage to load
text_search().waitForExistence(); //Start the timer timerStart("Regular");
text_search().click(); //Enter the search string in the search bar
text_search().setText("Search for Computer Books"); //Click on the more link
html_gbztms().click(); // Click on the Books link link_gb_10().click(); //Wait for
the Computers link to appear link_computers().waitForExistence(); //Click on the
Computers link link_computers().click(); //Check if the required book is listed try{
link_howToWriteAndPublishAScie().waitForExistence();
link_howToWriteAndPublishAScie().click(); logTestResult("Book Found", true); }
catch(Exception e) { logTestResult("Book Not Found", false); } //Go back to the
Books Page //Click on the more link /*This is where it goes wrong. Since these two
links were captured on the homepage, their recognition properties are not matching*/
html_gbztms().click(); //click on the Books link link_gb_10().click();
link_computers().waitForExistence(); //Click on the computers link
link_computers().click(); timerStop("Regular");

Figures 5 and 6 show the result log for this test script, which also includes the time taken to execute this script

Figure 5. First result log for the standard test script
First result log screen output

As the result log shows, the recognition for most of the objects, such as the required book link, is very weak. In Figure 6, you can see that the recognition for the More link and the book link is also weak. As Figure 5 shows, the first time that Rational Functional Tester tried to identify these two links, it was able to do so easily, but when the same links had to be clicked from a different page (the Books page), recognition was very weak. This is a classic case of why it's necessary to upgrade the recognition properties for captured objects.

Figure 6. Second result log for the test script
Second result log screen output

Figure 7 shows the time taken to run this script. The execution and recognition will improve only after the recognition properties for these objects are updated and the problematic properties are converted to regular expressions.

Figure 7. Time that it took to run this test script
Test run time in the result log

Figure 8 shows an example of failing recognition properties for an object. All of these properties must be updated or changed to regular expressions for the object to be recognized efficiently.

Figure 8. Failing recognition properties
Screen capture of recognition properties

Figure 9 shows how to convert the failing recognition property to a regular expression

Figure 9. Conversion of recognition properties
Screen capture

Design of the test script using the reusable objectless automation framework

Listing 2. Objectless automation framework code for the test script
//Start the browser
startBrowser("www.google.com"); //Wait for the page to load
document_google().waitForExistence(); //Start the timer timerStart("ROF"); //Get the
root test object RootTestObject root = getRootTestObject(); //Create text field to
represent the google search textbox WTextField googleSearch = new
WTextField(".class","Html.INPUT.text",".title","Search",root); googleSearch.click();
// googleSearch.setText("Search for books"); //Create a link to represent the more
link WLink more = new WLink(".class","Html.SPAN",".id","gbztms",root); //Click on
the more link more.click(); //Create a link to represent the books link WLink books
= new WLink(".class","Html.A",".text","Books",root); //Click on the books link
books.click(); //Wait for the page to load document_google().waitForExistence();
//Create a link to represent the Computers link WLink computers = new
WLink(".class","Html.A",".text","Computers",root); //Create the Computers link
computers.click(); //Wait for the page to load document_google().waitForExistence();
//Test whether the required book is displayed try{ WLink theBook = new
WLink(".class","Html.A",".text", "How to Write and Publish a Scientific
Paper",root); theBook.click(); logTestResult("Book Found", true); } catch(Exception
e) { logTestResult("Book Not Found", false); } document_google().waitForExistence();
//Refresh the more link object to accommodate changes in the page more =
more.refresh(PropertyList.REFRESH_MULTIPLE); more.click(); //Refresh the books link
object to accommodate changes in the page books =
books.refresh(PropertyList.REFRESH_MULTIPLE); books.click();
document_google().waitForExistence(); //Refresh the Computers link object to
accommodate changes in the page computers =
computers.refresh(PropertyList.REFRESH_MULTIPLE); computers.click();
timerStop("ROF");

Listing 2 shows the code for the same functional test script but using the reusable objectless automation framework (ROF). All that the test developer had to do here was import the framework into the development environment and use the custom objects available in the Java archive (JAR) file, such as WTextField or WLink, to create the objects on the fly, rather than going through the tedious process of capturing and maintaining the objects. These custom objects have several overloaded constructors that can be used to create these objects. Properties such as .class, .text, and so on can be passed to the constructors used in Listing 2, along with the values for those properties. Because the algorithm used by these custom objects uses these limited properties for recognition, the recognition time is faster. One of the constructors for these custom objects does not even require you to have any knowledge of the properties of the object. All you need to pass to the constructor is the text that you see on the screen, and it will create the required object for you. Using this framework drastically reduces development time, maintenance, and execution time for these test scripts. The best thing about this framework is that, because it is reusable, it gives all test teams a base that they can use to write their test scripts in almost no time.

The best thing about this framework is that, because it is reusable, it gives all test teams a base that they can use to write their test scripts in almost no time.

Code walkthrough

  • RootTestObject root = getRootTestObject();
    The RootTestObject represents a global view of the system being tested. It is used as the parent object using which the custom objects are created.
  • WTextField googleSearch = new WTextField(".class","Html.INPUT.text",".title","Search",root);
    Rather than recording and capturing the Google search text box object, the test developer simply uses the custom WTextField class to create an object representing the Google search textbox. Given that the custom class extends the appropriate GuiTestObject class that belongs to Rational Functional Tester, the tester can perform all of the operations that are possible to perform on a regular text box object captured from the AUT.
  • WLink books = new WLink(".class","Html.A",".text","Books",root);
    The WLink books, represents the Books link in the AUT. The custom objects have a refresh method that can be used to sync their properties with the current state of the system. This is how the books and more links recognition problem in the books page faced in the regular test script is avoided in real time, without touching any object or recognition property

Figure 10 shows the execution time for this script.

Figure 10. Time for running the reusable objectless automation framework test script
Result log for the objectless test script

As the log shows, there are no warnings or failures in object recognition. The required objects were created at runtime, used and then removed. The execution time is much faster than for the regular test script. The regular test script can achieve a comparable execution time only after the recognition properties of the captured objects have been updated.

Listing 3 shows the code for the same test script using the WYSIWYG custom objects. Using these methods, the custom objects can be created based on what you see on the screen. The test developer does not even need to know the recognition properties for the objects. This reduces development time even further and minimizes the interaction of the test developer with the objects in the AUT.

Listing 3. Objectless automation framework code for the test script
//Start the browser
startBrowser("www.google.com"); //Wait for the page to load
document_google().waitForExistence(); //Get the root test object RootTestObject
parent = getRootTestObject(); //Create text field to represent search bar WTextField
googleSearch = new WTextField(parent,"Search"); googleSearch.click(); //Create a
link to represent the more link WLink more = new WLink(parent,"gbztms");
more.click(); //Create a link to represent the books link WLink books = new
WLink(parent,"Books"); books.click(); document_google().waitForExistence(); //Create
a link to represent the Computers link WLink computers = new
WLink(parent,"Computers"); computers.click(); //Create a link to represent the book
WLink theBook = new WLink(parent,"How to Write and Publish a Scientific Paper");
theBook.click(); document_google().waitForExistence(); //Refresh the objects more =
more.refresh(PropertyList.REFRESH_SINGLE); more.click(); books =
books.refresh(PropertyList.REFRESH_SINGLE); books.click();
document_google().waitForExistence(); computers =
computers.refresh(PropertyList.REFRESH_SINGLE); computers.click();
  • WLink books = new WLink(parent,"Books");
    Rather than discovering the values for properties (such as .class and so on) for this object and then passing them to the constructor, the test developer just needs to pass the text associated with an object shown on the screen to create that custom object.
  • The tradeoff of this approach is that the execution time of the test script will increase, because the recognition processing time will increase.

Design of the reusable objectless automation framework

Framework structure

Figure 11 shows the folder structure for a sample framework.

Figure 11. Folder stucture for the framework
Screen capture of folder hierarchy
  • objProps
    This folder will be used to store all properties and constant values required for the widgets. The test developer can create property files specific to the AUT and keep them in this folder, as well.
  • testCases
    As the name suggests, this folder is meant to store all of the test cases for a specific AUT.
  • Widgets
    This is where all of the code for the widgets goes. After importing the framework into the development environment, the test developer will be able to use these custom objects to represent objects in any AUT and start writing automation test scripts right away, without capturing and updating any objects. The widgets can be extended to add extra functionality, as well.
  • Logs
    This folder will be used to store all of the logs for the test scripts.

Widgets

Listing 4 shows the sample code for a WLink widget.

Listing 4. Code for a WLink widget
package widgets; import objectProps.PropertyList; import
com.rational.test.ft.object.interfaces.TestObject; import widgets.ancestors.Widget;
public class WLink extends Widget { String
propName1,propName2,propValue1,propValue2,displayText; TestObject parentObject;
public WLink(String propName1,String propValue1,String propName2, String
propValue2,TestObject parentObject) {
super(getTestObject(propName1,propValue1,propName2, propValue2,parentObject));
this.propName1 = propName1; this.propName2 = propName2; this.propValue1 =
propValue1; this.propValue2 = propValue2; this.parentObject = parentObject; } /** *
Constructor to store an explicitly specified Link object * @param Link the
GuiTestObject from which to construct the widget. * */ public WLink(TestObject Link)
{ super(Link); } public WLink(TestObject parentObject, String displayText) {
super(getTestObject(parentObject,displayText)); this.displayText = displayText;
this.parentObject = parentObject; } public WLink refresh(String methodName) {
if(methodName.equalsIgnoreCase(PropertyList.REFRESH_MULTIPLE)) return new
WLink(propName1,propValue1,propName2, propValue2,parentObject); else
if(methodName.equalsIgnoreCase(PropertyList.REFRESH_SINGLE)) return
getTestObject(parentObject, displayText); return new WLink(parentObject,
displayText); else return null; } }
  • The WLink class extends the Widget class. It has three constructors:
    • The first constructor takes two properties and their values and a parent object. The properties are used to locate the object under the parent object. Thus, the closer the required object is in hierarchy to the parent object, the quicker it will be found.
    • The second constructor can be passed the actual link object from which to construct a new custom object.
    • The third requires only a string value related to the object, such as the text displayed on the link object or that on a button, for example. In some cases, the object might not have any visible text on it. For example, a text box with a label next to it that says Username. In this case, the text box can be constructed easily by specifying the offset. Offset refers to the position of an object relative to another object under the parent object. In this case, you can use the Username label as reference, add 1 as the offset, and construct the text box object.
  • The Refresh method is used to synchronize the properties of an existing object with the current state of the AUT. In this way, a Books link created on the Google home page will still function if used on the Books home page. This would not have been possible with the traditional object capture, unless the properties of the Books link were updated manually and converted to regular expressions.

Listing 5 shows a code snippet from the Widget class.

Listing 5. Sample code from the Widget class
/** * Clicks a dynamically located widget * * Call with e.g. *
Widget.click(propName1, propValue1, propName2, propValue2, parentObject); * where
propName and propValue indicate the property of that object and its value * @param
propName1 property type to search for (e.g. ".value") * @param propValue1 property
value for that type, ex: Search * @param propName2 property type to search for (e.g.
".class") * @param propValue2 property value for that type, ex: Html.A * @param
parentObject parent TestObject from which to search (e.g Table_HtmlTable_3() or
BrowserPage()). */ public static void click(String propName1, String propValue1,
String propName2, String propValue2, TestObject parentObject) throws
ObjectNotFoundException { TestObject to = null; //find the object to =
TestObjectFactory.findDescendantTestObject(propName1, propValue1, propName2,
propValue2, parentObject); to.waitForExistence(); //will throw
ObjectNotFoundException to caller if not found within default timeout new
Widget(to).click(); //to log it //unregister the object to.unregister(); } /** *
Finds a TestObject dynamically using properties * where propName and propValue
indicate the property of that object and its value * @param propName1 property type
to search for (e.g. ".value") * @param propValue1 property value for that type, ex:
Search * @param propName2 property type to search for (e.g. ".class") * @param
propValue2 property value for that type, ex: Html.A * @param parentObject parent
TestObject from which to search (e.g Table_HtmlTable_3() or BrowserPage()). *
@return the object attempting to find */ protected static TestObject
getTestObject(String propName1,String propValue1, String propName2, String
propValue2,TestObject parentObject) throws ObjectNotFoundException { TestObject to =
TestObjectFactory.findDescendantTestObject (propName1, propValue1, propName2,
propValue2, parentObject); //Make sure the object exists if (to == null) throw new
ObjectNotFoundException ("Requested Object could not be found"); return to; } /** *
Finds a TestObject dynamically using a string * @param displayText visible text
related to object(e.g. "Search Button") * @param parentObject parent TestObject from
which to search (e.g Table_HtmlTable_3() or BrowserPage()). * @return the object
attempting to find */ protected static TestObject getTestObject(TestObject
parentObject, String displayText) throws ObjectNotFoundException { TestObject to =
TestObjectFactory.findObjectWithText(parentObject, displayText, objectClass); //Make
sure the Object exists if(to == null) throw new ObjectNotFoundException ("Requested
Object could not be found"); return to;

The Widget class extends the GuiTestObject class. Thus, all Widgets will have functionality supported by the GuiTestObject class. Similarly, the WSelect class will extend the selectWidget class, which in turn will extend the SelectGuiSubitemTestObject class. The test developer can use the Widget classes as they are or also extend them in order to override existing methods and add new ones.

Listing 6 shows a code snippet from the TestObjectFactory class. This class holds the methods that help find and create objects dynamically at runtime. Although there can be a number of implementations to find objects dynamically, I have provided some basic sample implementations that can be used.

Listing 6. Sample code from the TestObjectFactory class
public static TestObject
findDescendantTestObject(String propName1,String propValue1, String propName2,
String propValue2,TestObject parentObject) { TestObject[] descendants =
parentObject.find(atDescendant(propName1, propValue1, propName2, propValue2));
return descendants[0]; } public static TestObject findObjectWithText(TestObject
parent, String displayText, String objectClass) {
if(parent.getClass().getSimpleName(). toString().equalsIgnoreCase(objectClass))
if(matchFound(parent, displayText)) { return parent; } RegularExpression regex = new
RegularExpression("/*",false); TestObject[] objList =
parent.find(atDescendant(".class",regex)); //see if we can find it further down the
tree TestObject returnVal = null; int index = 0; for(index = 0; index <
objList.length; index++) { returnVal = findObjectWithText(objList[index],
displayText, objectClass); if(returnVal != null) { break; } else { //this isn't the
right one so unregister it //objList[index].unregister(); } } //unregister anything
left in the list for(index = index + 1; index < objList.length; index++) {
//objList[index].unregister(); } return returnVal; } public static boolean
matchFound(TestObject parent, String displayText) { Hashtable rgnProps;
if(parent!=null) rgnProps = parent.getRecognitionProperties(); //props =
parent.getProperties(); System.out.println("RGN "+rgnProps); Set set =
rgnProps.entrySet(); Iterator it = set.iterator(); while(it.hasNext()) { Map.Entry
entry = (Map.Entry)it.next(); System.out.println(entry.getKey()+" = "
+entry.getValue()); if(entry.getValue().toString(). equalsIgnoreCase(displayText))
return true; } } return false; }
  • findDescendantTestObject() uses the Find method to locate any object matching the given properties under the parent object and returns the first such object.
  • findObjectWithText() uses recursion to locate the object with the given text under the parent object. It is possible for two or more objects with the same text to be present in the AUT. The method uses the class of the object to differentiate between multiple matching objects. The same implementation would work for the offset, as well. When an object with text visible on the screen is found, just add or subtract the offset from the index of the array of TestObjects and return the corresponding object.
  • The matchFound() method searches the recognition properties of all objects under the parent for the string. If it is not found, the same search can be extended to the other properties.

Conclusion

By using this framework, the test development team can drastically reduce their development time, minimize maintenance, reduce test script execution time, and reuse this framework for creating test scripts for any applications that Rational Functional Tester supports.


Download

DescriptionNameSize
Code for this articlereusable-objectless-automation-framework-code.rar5KB

Resources

Learn

Get products and technologies

  • Evaluate IBM products in the way that suits you best: Download a product trial, try a product online, use a product in a cloud environment, or spend a few hours in the SOA Sandbox learning how to implement Service Oriented Architecture efficiently.

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Information in your profile (your name, country/region, and company name) is displayed to the public and will accompany any content you post, unless you opt to hide your company name. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into Rational software on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Rational
ArticleID=782498
ArticleTitle=A reusable objectless automation framework for Rational Functional Tester
publish-date=01102012