Forum Discussion

iamraj09's avatar
iamraj09
Contributor
9 years ago
Solved

Can we replace whole object chain with single word?

Hello Everyone,

 

Right now I am working on performance test cases for a huge task which includes lot of objects, if I take the object chain for each and every object the script is becoming very messy. So is there any way in NameMapping, where we can use single word instead of entire object chain. 

 

For example:  

 

Instead of this:

 

Sys.Process("XXX").WPFObject("HwndSource: ChildWindow", "").WPFObject("ChildWindow", "", 1).WPFObject("NewProjectView", "", 1).WPFObject("Grid", "", 1).WPFObject("Grid", "", 2).WPFObject("Button", "OK", 1).ClickButton();

 

Can we use this way?

 

OK_Button.ClickButton();

  • joe_2's avatar
    joe_2
    9 years ago

    Well, I did it again... tried to tab indent something and closed the comment window instead.  I really hate this interface sometimes.

    Anyway, If you get two responses from me, that's why...

     

    So, what I saw looking at your example is that you were trying to Findchild an object named 

    WPFObject("Button", "New project", 1) this is the NAME of the object

     

    So your findchild line needs to be 

    Sys.Process("MyProcess").FindChild("Name", "WPFObject(""Button"", ""New project"", 1)",10).ClickButton();

    (understanding that the double quotes "" are the VBScript way of including quotes in a string value... The blue text is all one value.  If your scripting language does it another way then you'd need to change that... maybe to something like (.\"Button.\", where the .\ means the next character is part of the value you're specifying, and not meant to close the quote) 

     

    Or, you could shorten it a bit by using wildcards and just have

    Sys.Process("MyProcess").FindChild("Name", "*New project*", 10).ClickButton();

    which also does away with having to include the double quotes. 

     

    I usually use 'Name', 'ButtonText', 'Caption', or 'Value' as the property to search.You can see a complete list of available properties for a given object by looking at it in the object browser.  Pick any property that will positively identify the object you want out of all the other objects, and use that.  But keep in mind that some things, like an object's 'Handle' may seem unique, but they change every time the object is created.  Other properties like 'BackColor' are useless because they aren't unique to the desired object.

  • Another way to find the object is to create an array of properties and values that identify it specifically.

    ex:

     

     

    function GetObj(parent,propName,propValue,layer)
         set GetObj=parent.Findchild(propName,propValue,layer)
    end function
    
    sub test
       propName=Array("NativeCLRobject.Name","Index")
       propValue=Array("TheNativeCLRObj.NameValue",idx)
    
       set okButton=GetObj(Sys.process("myprocess").findchild("nativeClrobject.name","thisparent",2),propName,propValue,5)
    
      okButton.ClickButton
    end sub

    then just use the GetObj function withouth have to type so much

     

    better yet you can create a function to return the process

     

     

    private function App
       set app=sys.process("myProcess")
    end function
    function MainWindow
       set MainWndow=  App.FindChild("nativeclrobject.name","MainWindow",3)
    end function
    function GetObj(parent,propName,propValue,layer)
         if parent is null then
               set GetObj=null
         else
              set GetObj=parent.Findchild(propName,propValue,layer)
        end if
    end function
    
    sub Test
       set OKButton=GetObj(GetObj(MainWindow,"nativeclrobject.name","thisparent",parentlayer),propName,propvalue,layerValue)
      if Not isnull(OKButton) and OKButton.exists then
             okbutton.clickbutton
      end if
    end sub

     

     

    also dont forget that you can get the parent window of the object you want to interact with by using the GetObj to return the parent window, but care on this as it does not check if it finds teh object or not and assume.

     

    This is just a quick edit but i think it should work, the last if statement might be redundent but if the GetObj did return an object that does not exist then its not null

     

  • Why leave all the unwanted parts in the Alias map?

     

    I map objects, but either don't automatically create alias objects, or delete the alias version. Then just alias map the parts you're interested in. Makes it much neater.

     

    So for example:

     

    Object map:

     

    Calc

    > Calc Container

    >> Calc Panel

    >>> Calc Button Cluster

    >>>> Calc Button 3

     

    In the Alias map, I would only have:

     

    Calc

    > Calc Button 3

     

    All the intermediate parts, which are required but I'm not interested in, would be left out.

     

    Keeps the Alias map/tree much neater and makes your code complete for mapped Alias object much more concise. Think of the object map as the novel, and the Alias map as an abbreviated summary of it. You can also use multiple copies of single objects to create the illusion of separation in the Alias map. I do this a lot with web pages, tabs within pages etc. Give the multiple Alias copies different names and it makes the Alias map a much closer match for how you see the application in terms of the parts you actually use during the test.

     

    I tend to only map object to a fairly high level. Then use helper functions to locate the more detailed objects on the fly during run time. I can see why people like a simple function that searches for an ID all the way down from the sys object. But this method is not without it's drawbacks. In a complex app, with a lot of components, searching the entire object tree for an object many layers down can be very slow. Hit exactly this problem with a delphi application the other day. A simple find on the top the level sys object worked, but was very slow. Refined how it searches to make it a little smarter, and it's much faster. It's much like SQL. Simple, bad, SQL will work, but slowly. A smarter query will be way faster ....

12 Replies

  • joe_2's avatar
    joe_2
    Contributor

    Look for the FindChild method in TestComplete's help files...

     

    FindChild lets you search for a child item by looking for a property that has a specified value.

    So a FindChild statement that searched for "ButtonText" as the property and "Ok" as the specified value would look like this:

     

    Call Sys.Process("XXX").FindChild("ButtonText", "Ok", 10).ClickButton()

     

    *(VBScript)

     

    That searches 10 layers deep for child objects of the process XXX, with a button text of Ok, and then clicks the first one it finds that matches.  

     

    It's not one word, but it's a lot shorter than your example.

     

    The person that introduced me to this says he got his devs to include a property on their objects that contained a unique value for each object.

    In this way he could do Sys.Process("XXX").FindChild("MyProperty", VarX, 99) and get the specific object he wants with the exact same line every time, by just setting the value of the VarX variable.  (My devs were not interested in doing this, though, sadly for me)

  • sbkeenan's avatar
    sbkeenan
    Frequent Contributor

    Hi

     

    In a nutshell, yes it is possible.  When using NameMapping, simply enter what you want to call the control in the Mapped Name field.  I think you would need to be careful about a control that appears more than once in your application though, i.e. the [OK] button on one screen might not be the same [OK] button that appears on another!!

     

    Regards
    Stephen.

    • iamraj09's avatar
      iamraj09
      Contributor

      Hello 

       

       

       

       

      • sbkeenan's avatar
        sbkeenan
        Frequent Contributor

         

        Hi

         

        You're correct in that yes, you would have to use the Alias chain, which will be a little shorter, but, as an example, I have used the Windows calculator to help explain.  When I map, say teh '3' button, the Aliases section of the NameMapping file looks liek this:

         

        Aliases1.png

         

        In the above example, rather than using the suggested name 'btn', I made it 'btn3'.  Now, at this point, in order to reference the button object, I would need to use 'Aliases.calc.wndCalculator.CalcFrame.page32770.btn3'.

         

        To shorten this, I can simply drag the 'btn3' element to the top level so that it looks like this:

         

        Aliases2.png

         

        Now, I can use 'Aliases.btn3' to refererence the '3' button.  So I can use something like:

         

        Aliases.btn3.clickButton();

         

        Or, what I usually do is assign a variable to the object (I don't usually even bother shortening the Alias name), so I would have something like this:

         

        var btn3 = Aliases.btn3;
        //I can now simply use btn3 as a reference to the button.
        btn3.clickButton();

        without shortening the Alias chain, you could use:

         

        var btn3 = Aliases.calc.wndCalculator.CalcFrame.page32770.btn3;
        btn3.clickButton();

         

        I would also say that Joe_2 has a fine solution with the findChild method. It's really a matter of personal preference and the circumstances under which you are working.

         

        Personally, I prefer to use NameMapping, but the application I am usually testing is a Windows based desktop application that is not likely to change much.  However, when recently testing a web site. I found that the findChild method was a much better solution for me because the controls we constantly being moved up and down through different levels in the hierarchy.

         

        Hopefully this helps!!

         

        Regards

        Stephen.

  • joe_2's avatar
    joe_2
    Contributor

    You can shorten it a lot by using findchild...

    so your example would become

     

    Sys.Process("XXX").FindChild("ButtonText","Ok",10).clickButton();

     

    and from there you could write a script that contained a subroutine such as

    Sub ClickOk()

     

    • joe_2's avatar
      joe_2
      Contributor

      UGH!  I hate forum posting interfaces, sometimes.

       

      This one posted a partial response because I tried to tab in the text entry area.

       

      Disregard this post and look for the full one below, lol.

      • iamraj09's avatar
        iamraj09
        Contributor

        Hello joe_2,

         

        I have tried the way you said, but it says Object doesnt exist. This is what I have tried. Please correct me if I did anything wrong. 

         

        Sys.Process("MyProcess").WPFObject("HwndSource: mainWindow").WPFObject("mainWindow").WPFObject("LayoutRoot").WPFObject("headerBar").WPFObject("LayoutRoot").WPFObject("Grid", "", 1).WPFObject("ContentControl", "", 1).WPFObject("DockPanel", "", 1).WPFObject("StackPanel", "", 1).WPFObject("Button", "New project", 1)

         

        function OnObjectClicked()
        {
        Sys.Process("MyProcess").FindChild("Button", "New project", 10).ClickButton();

         

         

        Best Regards,

        Guru