Forum Discussion

scot1967's avatar
scot1967
Frequent Contributor
14 days ago

How To: Data Structures for Application Object Definitions

There are a few ways to identify objects and store their properties in TestComplete.  Using a NameMap with aliases is the most commonly documented and supported method.  If you are a script coder like me and prefer to code all your objects here are a few ways to accomplish this.  I have found these methods to be helpful in scenarios where you are testing a process flow across several applications or you just need to do some really custom coding to identify an object that is just difficult to work with.  The JSON method described here can be used by devs to send you a JSON object with identification properties and values you could build your tests around.  This type of flexibility is one of the reasons I like working in TestComplete.      

Library files containing data structure objects like JavaScript objects, Map objects, and JSON objects work well across multiple projects and suites.  These files are also nice to work with in source control.  Changes in any library file can be easily seen and tracked.  Each of these object types will integrate seamlessly with methods like FindChild and work equally well with desktop and web applications.  

  • JavaScript objects are directly usable in TestComplete scripts. 
  • Map objects have some advantages over JavaScript objects such as built in looping methods and properties. This adds some complexity in implementation as these methods must be used to access the data structure.
  • JSON is a 'portable' object definition.  Many different languages support JSON.  JSON requires a parse operation to return a JavaScript object prior to use in JavaScript. JSON would be preferred if the object definitions were created directly from the application code base by development.

Object Definition Examples

This code defines an object named customOptionObjDefs. It contains two UI object definitions: btnOK and btnCancel (It could contain many definitions).  The code below shows examples of a JavaScript Object, a JavaScript Map and a JSON Object and the code used with the TestComplete method FindChild.  

JavaScript Object

const customOptionObjDefs = {
  btnOK: { propertyNames:["WPFControlName","WPFControlText"] ,propertyValues: ["btnOK","OK"], depth: 16},
  btnCancel: { propertyNames:["WPFControlName","WPFControlText"] ,propertyValues: ["btnCancel","Cancel"], depth: 16}      
}
const btnOKDef = customOptionObjDefs.btnOK; // directly accessable 
const btnOKObject = parentObject.FindChild(btnOKDef.propertyNames,btnOKDef.propertyValues,btnOKDef.depth);

JavaScript Map Object

const customOptionObjDefs = new Map([
  ["btnOK", { propertyNames: ["WPFControlName", "WPFControlText"], propertyValues: ["btnOK", "OK"], depth: 16 }],
  ["btnCancel", { propertyNames: ["WPFControlName", "WPFControlText"], propertyValues: ["btnCancel", "Cancel"], depth: 16 }]
]);
const btnOKDef = customOptionObjDefs.get("btnOK"); // .get method
const btnOKObject = parentObject.FindChild(btnOKDef.propertyNames,btnOKDef.propertyValues,btnOKDef.depth);

JSON Object 

const customOptionObjDefs = {
  "btnOK": { "propertyNames": ["WPFControlName", "WPFControlText"], "propertyValues": ["btnOK", "OK"], "depth": 16 },
  "btnCancel": { "propertyNames": ["WPFControlName", "WPFControlText"], "propertyValues": ["btnCancel", "Cancel"], "depth": 16 }
};
const btnOKDef = JSON.parse(customOptionObjDefs).btnOKDef; // .parse method
const btnOKObject = parentObject.FindChild(btnOK.propertyNames,btnOK.propertyValues,btnOK.depth);

I prefer to organize object definition libraries in files by application and form and 'importing them in scripts or in the class structure of a project using 'require'.  These files can be stored externally to the project, shared and organized as desired.

const orderEntryDefs = require("orderEntryDefinitions");

Sometimes application objects are not well named or require a variable to be calculated dynamically in order to be defined. These objects are a challenge to define for automation and usually lead to brittle code.  In such cases code is written to determine the values needed for definition and passed directly to the FindChild method or a 'helper' class method or function.

In most cases a helper class method or a function is used to create and return the objects defined in the definition libraries.  The use of a 'helper' class method or function will provide a layer of abstraction, centralized error handling and more modular code.  The file(s) containing these functions or methods would also be imported for each script using 'require'.

Conclusion

The use of data structures provides a modular way to define and create instances of objects for automation scripts.  These objects can be easily looped over to find and create container objects for entire forms or very complex object like grids or trees. I have found that creating all available objects for a form or in groups if the form changes dynamically and storing them in a object makes the script easier to write and to read.  

 

Scott Holmes
WW Wood Products Inc.

No RepliesBe the first to reply