Forum Discussion
Relative UI Testing newbie here - been working with TC for about 5 months now. Started from scratch having to learn the ins-and-outs of TC, python, and how TC integrates with our Windows application objects, etc. Since, I started out fresh, I was able to design the process however I wanted, and I believe I came up with a pretty unique design to aid in creating/updating tests faster and easier, very little maintenance, and allows anyone to create tests without first learning TC/python/etc. I compared my design to another teams (that's been using TC for 5 years now) and if my calculations are correct, I'll be able to reduce the amount of code we have to maintain by over 95% or more. Not sure if my design will stand the test of time yet, but I'm very happy with the way it's come out so far. :smileyhappy:
btw...the hardest thing we've come across so far is VST's (Virtual String Trees) - in a Delphi Development environment. We did a ton a reseach (on here and other sites) trying to find a good solution. We finally figured out a way handle them, but I wouldn't say it's a great solution. If anyone has experience with VST's (especially using Python), I would definitely like to hear more about your experiences and how you figured out solutions for them.
- Simon_InT6 years agoContributor
Hi dpaulus , I'm also testing Delphi applications that use Virtual String Trees a lot and struggle with it. As of yet I have not been able to work out which Object Mapping on the project property to use in order to identify properties within the VST. When I use the object spy and select an VST object it returns the error "Error: An exception occurred: 0x0EEDFADE; class: EComponentError; description: 'TVirtualStringTree has not been registered as a COM class'" on the ComObject property. Are you able to share how you have been handling them?
- dpaulus6 years agoOccasional Contributor
Oh man - yeah, those are a pain. Currently, we ended up having our developers write a little function to do the dirty work for us (see code below), and then we call the function from our TC scripts as needed. I'm not a big fan of this method, and I did write a script that could handle it at one point, but it didn't work for every situation. I don't think this does either, but it's a bit easier to call. If you choose to use this, then you'd have to have your developer put it in their code (obviously, not ideal), and then call it from a debug version of the app your testing. Regarding your error, seems like there's a possible problem there, but a general idea here is to use the Object Browser, instead of Object Spy, because it'll give you a lot more detail about all the components, not just the one you highlighted. I almost exclusively use the Browser now when trying to lookup an object.
Delphi Code:
// Data can look like any of 'Value' or '[Value]' or '[Value][SubValue]' as many levels deep as needed. // The result they want back is |[n1]|[n2], etc. // where n1 is the node index of Value, // n2 is the child node index of SubValue, etc. // note the pipe. |[x] for each value returned. function TVirtualStringTree.GetNodeIndex(Data: String; VisibleColumnIndex: Integer): String; // for QA var Path: String; Column: Integer; function FindChild(aNode: PVirtualNode; aData: String): PVirtualNode; var Node: PVirtualNode; Idx: Integer; begin Result := nil; idx := -1; if aNode = nil then Node := GetFirst else Node := aNode.FirstChild; While Node <> nil do begin idx := idx + 1; if Text[Node, Column] = aData then begin Path := Path + '|[' + IntToStr(idx) + ']'; Result := Node; break; end; Node := Node.NextSibling; end; end; var lData: String; Node: PVirtualNode; aTS: TStringList; ColumnsArray: TColumnsArray; begin if Data <> '' then begin aTS := TStringList.Create; try ColumnsArray := Header.Columns.GetVisibleColumns; Column := ColumnsArray[VisibleColumnIndex].Position; Path := ''; lData := Data; if lData[1] = '[' then lData := Copy(lData, 2, MAXINT); if lData[Length(lData)] = ']' then lData := Copy(lData, 1, Length(lData) - 1); aTS.Text := Trim(StringReplace(lData, '][', #13#10, [rfReplaceAll])); Node := FindChild(nil, aTS[0]); aTS.Delete(0); while (aTS.Count > 0) and (Node <> nil) do begin Node := FindChild(Node, aTS[0]); aTS.Delete(0); end; if Node <> nil then Result := Path else Result := ''; finally aTS.Free; end; end; end;
Not sure what language your scripts are in, but ours is in Python, and below is how it's called...
#----------------------------------------------------------------------------- # Process TVirutalStringTree #----------------------------------------------------------------------------- def Process_TVirtualStringTree(ItemObjStr, ItemValue): try: ColIndex = '0' # if '>' is found, split if ItemValue.find('>') != -1: ColName, ItemValue = ItemValue.split('>') # get list of column headers ColumnList = ItemObjStr + ".ContentToText(3,',')" ColumnList = eval(ColumnList) ColumnList = ColumnList.split(',') # loop through header list until value is found, then set index index = 0 index2 = 0 ColIndex = -1 for item in ColumnList: index = index + 1 item = item.strip() if item == ColName: ColIndex = str(index2) break index2 = index2 + 1 # if ColIndex is never updated, then the column was never found - log error if ColIndex == -1: Log.Error('Column not found') # pass column value and column index to John's routine NodeIndex = ItemObjStr + '.GetNodeIndex(' + '\'' + ItemValue + '\'' + ',' + ColIndex + ')' NodeIndex = eval(NodeIndex) # if john's routine returnes -1 then we log an error if NodeIndex == '': Log.Error(ItemValue + ' not found') ItemValue = NodeIndex return ItemValue except Exception as Error: TerminateDueToError('Process_TVirtualStringTree', str(Error))
Hope this helps.
- Simon_InT6 years agoContributor
Thanks for this. I use JScript for scripting but I'll discuss this with our Delphi developers and see if we can come up with a similar solution.
Related Content
Recent Discussions
- 14 hours ago