We've moved!
Finally we've moved to a different platform.
Check us out at www.AdvancedQTP.com
'coz we're programmers, not users
Today we'll do another round of our (actually – mine) favorite activity - VBScript bashing. Specifically, we're going to talk about VBScript's inability to work with certain Interface objects. This question in SQAForums made me understand that I'm not the only one who deals with these problems, and that maybe it's time to share my workarounds.
Here're some quick facts: While VBScript is quite a backwards programming language (hence the "Script" in VBScript), it can work with sophisticated objects that were written in other languages. So, if you got an object with a COM interface, you can create it via the CreateObject command, and use it with your everyday VBScript commands. Moreover, if you write your own .Net DLL, you can create it within QTP's VBScript context via the DotNetFactory command. This can fool us into believing that life is indeed good, and that there's no object too complicated for VBScript and QTP.
BUT, this turns out to be wrong. There are objects which are too complex for VBScript, and as a result, too complex for QTP. These objects are certain types of Interfaces, which wrap around the application's actual objects and hide them from VBScript. If you have no idea what an interface is, don't worry – it's not very important for our purposes. The bottom line is that very complex applications are prone to work with interfaces, which means that you won't be able to access their internal object structure.
To give an example which bugged me for the better part of a year, if you're trying to automate an ESRI (geographic) based application, and you need the map's coordinates, you're kinda screwed. You can try accessing the properties of the map object all you want; it won't do you any good, because it's an interface, not a "regular" object.
So, how can you know if the object you're trying to access is an interface? You could read the object's documentation, but a much quicker way is trying to access it from QTP. Run a script which can lead you to the object, and pause it. Go to the debug window, and write the path to the run-time object. You'll see that QTP marks it as (Object). But now, try accessing the some internal property of this object (e.g. QTPObject.Object.SuspectROObject.SomeProperty). It doesn't really matter which property, nor if the object even has this property, because you'll see QTP spits out an "Object needed" error. This means that even though QTP knows this is an object, it can't access it. These objects will appear as var-type 13 (vbDataObject).
For now, this problem only surfs with complex applications and controls. However, it's only reasonable that these objects will become more and more dominant in the coming years, so be prepared.
OK, we've got an object which holds much needed information, but we can't work with it in QTP. How can we workaround that? I've found 3 methods for doing that:
Try finding a way to make it work: I don’t KNOW these objects are inoperable in VBScript, I've just given up finding a way to do so. It took me the better part of a year to give up, and I would LOVE to hear of some real solution to the problem.
Write extensibility: QTP .net add-in has a remarkable feature, which allows you to write your own QTP shell for unknown .net objects. The extensibility works with the .net objects within a C# context, which means that it can rise above any VBScript limitation, and specifically, it can work with these problematic objects. You can find out more about writing extensibilities in the QTP help files.
Write a custom DLL to do your dirty work: This is quite similar to the extensibility workaround, though it's much simpler and easy to implement. You can write a .Net function which receives the problematic object, works with it, extract the needed information from it, and returns it in a form which VBScript can work with. This is usually not a problem since at the end of the day, deep down the object hierarchy, you probably only need a string, number, or array value (e.g., the object is an abstract geographic point, but you actually need an X double value, and a Y one, easily structured as an array).
Once you got your function written packed as a .Net DLL, you can create it within QTP (using the DotNetFactory utility object). Then it's very simple to pass the problematic object to your function, get the answer, and continue the script without any problems.
Labels: VBScript
Hey all,
The site will undergo a major upgrade in the coming week (or two): immigrating to my own server, better platform (Wordpress 2.2), a structued Knowladge Base, better loading time, and much more. For me the most importent upgrade will be an automatic code highlighter, which will allow me to publish articles in 20 sec., instead of an hour.
I hope this will all happen by this Sunday, but there're many factors involved - my work, the students strike, the coming holiday etc.
Anyways, heads up.
Labels: General, Site Updates
It's very easy to control the entry gate to an action or function - there's only one way to enter them. However, the number of exit gates can vary widely from 1 to many according to the inner logic of the action/function. Today I'm going to address managing multiple exit gates in actions and functions.
When everything goes according to plan, an action flow tends to be very simple. There may be inner loops, If or Select switches, but for the most part, the flow just runs straight down to the last line. The problems usually appear when… well, problems appear. If an application error occurs, or even just an unexpected business logic behavior, there might be no escape from immediately exiting the action. There's no point trying to input 20 data fields, if the form they're in didn’t even open, is there?
Thankfully, the nice guys and gals at Mercury have taken this into account, and have provided us with the ExitAction and ExitActionIteration commands. So usually we've got something like the following:
Now that we've covered some of the basic issues regarding Descriptive Programming, I can elaborate further, on a more personal note. I've picked up some coding habits during my days, and some of them are DP related. Habits are arbitrary by definition, so there may indeed be better ways of doing things out there. However, I see no harm in pointing out a thing or two:
I don't trust regular expressions in DP
When we create a static description object, we can define the identification values by using Regular Expressions:
Labels: Descriptive Programing
Due to the gross incompetence of the Israeli Internet Association (ISOC-IL), the Hebrew version of the site is undergoing DNS problems. It seems that in 2007, DNS updates are done manually, via FAX (I shit you not).
Update to the update
Finally, someone got out of his chair and solved the problem. The Hebrew site is now available in www.AdvancedQTP.co.il.
All the best,
Combustible Moo.
Labels: Site Updates
To the hebrew version of this post
Before we can move on to some of the more advanced subjects, it's probably better if we ensure we're all speaking the same language. So now we'll create a proper base-line for some basic QTP techniques (though even they are sometimes referred to as "advanced"). One of the most profound techniques necessary for advanced QTP usage is "Descriptive Programming" (DP).
General Background
When using DP, we're bypassing the native object repository (OR) mechanism, which may have many advantages (easy to generate and read code, good data organization, etc.), but is extremely not flexible. We'll examine situations in which the OR's advantages are outweighed by the DP's flexibility.
In order to fully grasp how DP actually works, it's best to first understand how QTP's native OR works. The native OR is not some complex and deep mechanism, but rather a simple way of keeping groups of properties and values. To clarify: Whenever we add a certain window to the OR, QTP gathers the window's properties (e.g. – Height="400", Title="New Entity", vbName="NewEntityID"), and stores them as a group of property-value pairs. When QTP runs the script, it compares these properties and values to the "physical" objects, and finds (or fails to find) the object which they describe. Nothing mystical about it.
Usage
In DP, we're "manually" specifying the properties and values by which the relevant object will be identified. This way QTP won't search for the properties data in the OR, but will take it from the DP statement. This can be done in a fast, ad-hoc way, in the very line of the command we want to execute:
The syntax is: Class(PROPERTIESSTRINGS).Command, where PROPERTIESSTRINGS specifies the properties and values for the object's identification. For example:
VBWindow("property1:=value1", "property2:=value2").Click
VBWindow("property1:=value1").VBCheckBox("property2:=value2").Click
'----Create Object----'
Dim oDesc
Set oDesc = Description.Create
'----Set ID properties & values---'
oDesc("property1").Value = "value1"
oDesc("property2").Value = "value2"
'----Use and reuse the description object---'
VBWindow(oDesc).Type "Something"
'…
'…
VBWindow(oDesc).Close
'----Release description object---'
Set oDesc = Nothing
VBWindows(oDesc).VBCheckBox("vbname:=DPIsCool").Set "ON"
Dim oDesc
Dim oChildren
Dim i
oDesc = Description.Create
oDesc("micclass").Value = "VbCheckBox"
'----We could've left the oDesc object blank, To get all objects----'
Set oChildren = VBWindow("Main").ChildObjects(oDesc)
'----Now oChildren holds the checkboxs' collection----'
'----Run through the collection----'
For I = 0 to oChildren.Count-1
'----Set the specific checkbox to "ON"----'
oChildren(i).Set "ON"
Next
Labels: Basic, Descriptive Programing, Examples, General