TestComplete has lots of built-in features to operate with properties and methods of tested object. As was mentioned in a previous section, all wrappers over controls of AUT are implemented in components. This part of codebase is much often subject of changes from release to release and usually those changes are unpredictable. One day, our team was not ready to run in-time full regression tests against fresh build because of recent changes in DOM tree for many objects. We were need to invite something robust, not silent and not verbose; this mechanism should report something into logs when a control was not found directly but it should attempt to recognize that control using set of provided properties. We developed robust pattern that posses obvious advantages:
- Easy code maintenance: to call an object located on a page, just write something like this:
var conext = Common.GetAppTop();
var res = recognizeControl ( w.Panel(0).Panel(0).Panel(0).TextNode(0).Link(0), ["ObjectType","innerHTML"], ["Link","Log in"], context);
- Robust change management: if object direct path was changed on a page, test log will report an error but the control will be found indirectly either within a particular html element (e.g. cell, row) or within whole web page.
- Automatic checking of correspondence between properties values of direct object path and specified object properties for indirect search. In this way, we achieve discipline of test development process and may reveal hidden mismatches. For instance, this code can reveal that “Log In” link has changed innerText property to “Log Innn” but test will continue work with this control because direct object path is correct.
In conclusion, the proposed approach of object recognition provides checking relevant properties and expected object location simultaneously. The code snippet of the proposed pattern is presented below.
Figure. Test automation framework
Code snippet of pattern for object recognition and verification on the fly in JavaScript:
function recognizeControl( directPath, _propCollection, _propValues, topParentPath )
{
try
{
var res;
if ( typeof ( _propCollection ) == 'string' )
{
if ( Init.GetCurBrowser() == "firefox" )
_propCollection = _propCollection.replace( /innerText/g, "innerHTML" );
propCollection = _propCollection;
}
else
propCollection = Arrays.ConvertArray ( _propCollection );
if ( typeof ( _propValues ) == 'string' )
propValues = _propValues;
else
propValues = Arrays.ConvertArray( _propValues );
if ( TypeTraits.IsFit( directPath ) && directPath.Exists )
{
if ( TypeTraits.IsFit( _propCollection ) && TypeTraits.IsFit( _propValues ))
PropMatch ( directPath, _propCollection, _propValues );
return directPath; //specified direct link is returned
}
var res;
if ( TypeTraits.IsFit( topParentPath )&& topParentPath.Exists )
res = topParentPath.FindChild( propCollection, propValues, 200 );
if ( TypeTraits.IsFit( res )&& res.Exists )
return res; //the object was found located within the Parent object
else {
if ( TypeTraits.IsFit( topParentPath ) && topParentPath.Exists )
Log.Warning( 'Can`t find child with property name [ ' + propCollection.toString() + ' ]; and property value [ ' + _propValues.toString() + ' ]; on parent object;',' Parent:\r\n' + topParentPath.FullName + '\r\n Page:\r\n' + GetPage().URL, 300, f );
res = GetPage().FindChild ( propCollection, propValues, 200 );
if ( TypeTraits.IsFit( res ) && res.Exists )
return res; //the object was found located within the Page
else
Log.Error( 'Cant find child with property name [ ' + _propCollection.toString() + ' ]; and property value [ ' + _propValues.toString() + ' ]'+'');
}
}
return false; //default returning if nothing was found
}
catch ( ex ) {
Log.Error ( 'Exception occurs in Splitter.recognizeControl', ex.description, 300, f );
Init.exceptionHandler( arguments );
return false;
}
}
// PropMatch validates if object having direct path has predefined properties as alternatives;
function PropMatch ( directPath, _propCollection, _propValues ){
var str;
try {
if (( typeof _propCollection == 'string' ) || ( typeof _propCollection == 'number' ))
_propCollection = new Array( _propCollection );
if (( typeof _propValues == 'string' ) || ( typeof _propValues == 'number' ))
_propValues = new Array( _propValues );
for ( var i = 0; i < _propCollection.length; i++ ){
var iProp = _propCollection[i].toString();
var iProp = eval ( directPath.FullName + "." + iProp + ";" );
if ( typeof iProp != 'undefined' ){
var hasText; // Boolean flag for text existing
if ( _propValues[i].indexOf ( '*' ) == -1 ){
hasText = ( iProp == _propValues[i]);
}
else
hasText = true;
if ( !hasText )
Log.Warning ("The direct path to object [" + directPath.FullName + "] has property [" + _propCollection[i] + "] value [" + iProp + "] but expected value is [" + _propValues[i] + "]");
}
else
Log.Warning ( "Object [" + directPath.FullName + "] has no property [" + _propCollection[i] + "]");
}
}
catch ( ex ){
Log.Error( 'Exception occurs in Splitter.PropMatch', "The direct path to object [" + directPath.FullName+"] has property [" + _propCollection.toString() + "] value [" + iProp + "] but expected value is [" + _propValues.toString() + "]", 300, f );
return false;
}
}


No comments:
Post a Comment