Forum Discussion

efuller's avatar
efuller
Occasional Contributor
7 years ago
Solved

Using Xpath in TestComplete

Hello Everyone, 

 

As we're getting deeper into using TestComplete we noticed through the name mapping that TestComplete doesn't really pull information that we would want it to so it can identify objects. For example,for our sign out button it pulled ObjectType, href, and ObjectIdentifier.

 

To me, that doesn't seem like concrete evidence that is going to be the sign out button and especially down the line it will be horrendous to try and maintain, as the hierarchy is already getting messy and confusing.

 

So our solution was to create scripts to house all of the buttons we would use and their xpaths to call them. Easily maintened, easily navigated. 

 

But the problem I'm facing right now is (the code will be wrong) I'm trying to do something like this:

 

Script: Buttons

SignOut_Button = //*[@id='signout']/div[1]

 

Script:ClickTheButton

page.Buttons.SignOut_Button.Click();

 

I haven't been able to find any documentation on how we would be able to make this work. Is anyone able to point me in the right direction? Or is the name mapping part of the program going to be impossible to circumvent? 

  • NameMapping is not impossible to circumvent... but something to keep in mind is that the properties that TestComplete uses automatically are not fixed in stone.  You can edit, add, remove, adjust, etc. the properties used to identify objects to meet what you're looking for.  As I've mentioned many times, I rarely stick with what TestComplete gives me automatically and usually end up doing some sort of modification.

     

    Keep in mind that TestComplete's identification criteria is two-fold.

     

    1) Find the parent object... at the Root, that's NameMapping.Sys.  For additional layers, like your application, it starts with NameMapping.Sys and then looks at the children.  So, the first criteria is "Does the parent have a child..."

    2) Second criteria "...with properties that match this set."  So, you may have multiple objects that have the exact same properties... but if they are children of different parent objects, TestComplete sees them as distinct objects.  But the properties themselves are key to making sure that the CORRECT child object is identified.  

     

    So, for example, you have objectidentifier which COULD be something ugly like MainContent_divMain_divSubMain_btnSignOut.  Which, really, is ugly because, if the page changes in hierarchy, that could change.  Howerver, you can wild card it... I'd assume that, for the specific parent of the signout button, there would only be one object that would match "*btnSignOut".  Likewise your href...  test domains shift and change so the root URL may be different... but the rest of the path, if developer properly, should be the same... so, you're href could be edited to "*/signout.aspx" or whatever it is.

     

    But you are not limited to using only ObjectType, href, or ObjectIdentifier... ALL the properties of the object are available to you for identification so, if there are properties that you would prefer to use, feel free to use them instead.

     

    Now, if you're going to use XPath, you will want to us FindChildByXPath (https://support.smartbear.com/testcomplete/docs/reference/test-objects/members/web-objects/findchildbyxpath-method-web-object.html).   In this case, your code would be something more on the lines of

     

    browser = Sys.Browser(*);

    page = browser.Page('myURL');

    SignOut_Button = page.Buttons.FindChildByXPath('/*[@id='signout']/div[1]')

    if (SignOut_Button.Exists) {

        SignOut_Button.Click();

    }

     

     

    This pretty much completely by passes the NameMapping.  Now, you can organize things into code units and so forth to build your own object identification structure, but, in your example, "Buttons" is not a child of the page... Button is a code unit that you are using to bring in the SignOut_Button object... so, you just simply need to click on the object.

     

    One thing of note... FindChildByXPath is slower than NameMapping in object identification because it does need to look at the whole page.  NameMapping, once an object is found, it remains in cache until the cache is refreshed or until it needs to be found again.  I would highly suggest that you look into using NameMapping for your project... but as mentioned above, work with the properties exposed by TestComplete.  If you need help in figuring out unique identification, please feel free to ask and we'll help as best we can.  

  • tristaanogre's avatar
    tristaanogre
    7 years ago

    One other note:

     

    Under Tools | Options | Engines | NameMapping there's an option to "Use extended find when possible" that, by default, is checked.  Extended find is a POWERFUL way of collapsing unnecessary identification hierarchy in your NameMapping tree... but it can cause a mess if it's left to it's own devices.  I prefer to turn that option OFF and, if I need to use Extended Find, I can apply it to the objects I need it to manually.  It sounds like, with "the hierarchy is getting messy" comment from you that it could be that Extended Find is collapsing a bunch of stuff making it hard to make heads or tales out of what objects belong to which parents.  Turn the option off and try remapping some of your stuff.  The hierarchy will make more sense then....

     

    ... and then do any "collapsing" you need to do using the Aliases part of NameMapping to compress long object chains into shorter identifiers.

4 Replies

  • AlexKaras's avatar
    AlexKaras
    Champion Level 3

    Hi,

     

    Actually, search by XPath is the worst approach in TestComplete's world and should be used as the last resort only.

    Several points of top of my head:

    -- According to my direct measurements, search by XPath is usually 3-5 times slower than search via Namemapping/FindChild();

    -- XPath is case sensitive (translate() function makes XPath expression hardly readable);

    -- XPath does not support regular expressions (try to write an XPath looking for either IMG or CANVAS element on the page);

    -- If sought for element has id specified, then, as for me, "//*[@id='signout']" XPath is not better in any area than .FindChild("id", "signout", 1000). And if the sought for object has no unique and stable identification attributes, than "//*[@id='signout']/div[1]" XPath is equally unclear as .FindChild("id", "signout", 1000).Panel(1);

    -- XPath may return native DOM object. This object will not have TestComplete-provided methods and properties (like .Exists and other). So the check like soughtForObject.Exists will crash and you will have to check first if the returned object is native DOM or TestComplete one and code accordingly.

  • tristaanogre's avatar
    tristaanogre
    Esteemed Contributor

    NameMapping is not impossible to circumvent... but something to keep in mind is that the properties that TestComplete uses automatically are not fixed in stone.  You can edit, add, remove, adjust, etc. the properties used to identify objects to meet what you're looking for.  As I've mentioned many times, I rarely stick with what TestComplete gives me automatically and usually end up doing some sort of modification.

     

    Keep in mind that TestComplete's identification criteria is two-fold.

     

    1) Find the parent object... at the Root, that's NameMapping.Sys.  For additional layers, like your application, it starts with NameMapping.Sys and then looks at the children.  So, the first criteria is "Does the parent have a child..."

    2) Second criteria "...with properties that match this set."  So, you may have multiple objects that have the exact same properties... but if they are children of different parent objects, TestComplete sees them as distinct objects.  But the properties themselves are key to making sure that the CORRECT child object is identified.  

     

    So, for example, you have objectidentifier which COULD be something ugly like MainContent_divMain_divSubMain_btnSignOut.  Which, really, is ugly because, if the page changes in hierarchy, that could change.  Howerver, you can wild card it... I'd assume that, for the specific parent of the signout button, there would only be one object that would match "*btnSignOut".  Likewise your href...  test domains shift and change so the root URL may be different... but the rest of the path, if developer properly, should be the same... so, you're href could be edited to "*/signout.aspx" or whatever it is.

     

    But you are not limited to using only ObjectType, href, or ObjectIdentifier... ALL the properties of the object are available to you for identification so, if there are properties that you would prefer to use, feel free to use them instead.

     

    Now, if you're going to use XPath, you will want to us FindChildByXPath (https://support.smartbear.com/testcomplete/docs/reference/test-objects/members/web-objects/findchildbyxpath-method-web-object.html).   In this case, your code would be something more on the lines of

     

    browser = Sys.Browser(*);

    page = browser.Page('myURL');

    SignOut_Button = page.Buttons.FindChildByXPath('/*[@id='signout']/div[1]')

    if (SignOut_Button.Exists) {

        SignOut_Button.Click();

    }

     

     

    This pretty much completely by passes the NameMapping.  Now, you can organize things into code units and so forth to build your own object identification structure, but, in your example, "Buttons" is not a child of the page... Button is a code unit that you are using to bring in the SignOut_Button object... so, you just simply need to click on the object.

     

    One thing of note... FindChildByXPath is slower than NameMapping in object identification because it does need to look at the whole page.  NameMapping, once an object is found, it remains in cache until the cache is refreshed or until it needs to be found again.  I would highly suggest that you look into using NameMapping for your project... but as mentioned above, work with the properties exposed by TestComplete.  If you need help in figuring out unique identification, please feel free to ask and we'll help as best we can.  

    • tristaanogre's avatar
      tristaanogre
      Esteemed Contributor

      One other note:

       

      Under Tools | Options | Engines | NameMapping there's an option to "Use extended find when possible" that, by default, is checked.  Extended find is a POWERFUL way of collapsing unnecessary identification hierarchy in your NameMapping tree... but it can cause a mess if it's left to it's own devices.  I prefer to turn that option OFF and, if I need to use Extended Find, I can apply it to the objects I need it to manually.  It sounds like, with "the hierarchy is getting messy" comment from you that it could be that Extended Find is collapsing a bunch of stuff making it hard to make heads or tales out of what objects belong to which parents.  Turn the option off and try remapping some of your stuff.  The hierarchy will make more sense then....

       

      ... and then do any "collapsing" you need to do using the Aliases part of NameMapping to compress long object chains into shorter identifiers.

    • Marsha_R's avatar
      Marsha_R
      Champion Level 3

      How about NameMapping along with Aliases, because those will be some ugly names to deal with and probably easy to mistype?