Thursday 26 July 2012

QTP Run As Example

The following code demonstrates how to use a Run As command to open applications within QTP.

An example of calling the function would be:

Call RunProcessAs("dave@domain","password","C:\Program Files (x86)\Internet Explorer\iexplore.exe")

The key thing to note is that the user and domain are passed in as one parameter (you could modify this so that they are not).

Here is the function:

'Function Name:  RunProcessAs
'Function Purpose: Runs a process using the "RunAs" command.
'Input Parameters: strUserAtDomain - the user and domain to use. e.g. Test UserName@DOMAIN
'     strPassword - Password for the username. e.g. Password
'     strApplicationLaunchParams - Launch details of the program as if it were being launched in a cmd window. 
'             e.g. C:\Program Files (x86)\Internet Explorer\iexplore.exe www.google.com
'Creation Date:  July 7th 2012 by David Hartley
'Modification Date: 
Function RunProcessAs(strUserAtDomain,strPassword,strApplicationLaunchParams)
 Dim strThisFunctionName : strThisFunctionName = "[Function RunProcessAs] " 
 Dim strRunAs
 Dim objFSO
 Dim strBatFilePath
 Dim objBatFile
     Dim objDesc,objDescCol
 Dim n
 Dim strBatWindow


 'An example of the desired launch string would be:
 ' runas /user:"test UserName@DOMAIN" "C:\Program Files (x86)\Internet Explorer\iexplore.exe www.google.com"

 'Construct the RunAs launch string:
 strRunAs = "runas /user:" & chr(34) & strUserAtDomain & chr(34) & " " & chr(34) & strApplicationLaunchParams & chr(34)

 'Now create a bat file to launch this from - the bat file will allow us to create our own cmd window for this process.
 Set objFSO = CreateObject("Scripting.FileSystemObject")
 strBatFilePath =  objFSO.GetSpecialFolder(2) & "\QTPRunAs.Bat"

 'delete the file if it exists
 If objFSO.fileexists(strBatFilePath) Then objFSO.DeleteFile(strBatFilePath)

 'now create the bat file
 Set objBatFile = objFSO.OpenTextFile(strBatFilePath, 8, True)
 Call objBatFile.WriteLine ("title QTP RunAs")
 Call objBatFile.WriteLine (strRunAs)
 Call objBatFile.WriteLine ("timeout 10")
 objBatFile.Close  
 
 'before we run the bat file, just check there are no already opened instances
 Set objDesc = Description.Create
 objDesc("micclass").value = "Window"
 objDesc("regexpwndtitle").value = "QTP RunAs"     
 Set objDescCol = Desktop.ChildObjects(objDesc)
 If objDescCol.count >0 then
  For n = 0 to (objDescCol.count -1)
   objDescCol(n).close
  Next
 end if

 'Now run the .bat file
 systemutil.Run strBatFilePath
 wait(1)
 strBatWindow = "regexpwndtitle:=QTP RunAs" 
 Window(strBatWindow).Activate 
 Window(strBatWindow).Type strPassword
 Window(strBatWindow).Type  micReturn 
 wait(2)

End Function

Wednesday 2 May 2012

Vbscript to Shutdown a PC

Here's a quick script I put together that will shutdown a PC after a defined period of time - it basically acts as a free shutdown timer. You'll need sufficient permissions to do this, check that you can execute the "shutdown" command from a dos prompt and you should be fine.
Just copy and paste this code into a text file and rename the extension to .vbs.

Code:
Call WaitRoutine() 

Sub WaitRoutine()

                Dim intWaitedTime
                Dim intMinsToWait : intMinsToWait = -1
  Dim objshell


   set objShell = CreateObject("WScript.Shell") 
                strAnswer = InputBox("How many minutes do you wish to wait?","Shutdown Computer")
                on error resume next
                intMinsToWait = cint(strAnswer)
                if (intMinsToWait = -1) or (strAnswer = "") then
                                msgbox "Shutdown Cancelled.",vbokonly + vbexclamation,"Shutdown Computer"
                                exit sub
                end if               

                NewDate = DateAdd("N", intMinsToWait, now())
                If msgbox("This will shutdown the computer at " & NewDate & ". Continue?", vbquestion + vbyesno,"Shutdown Computer") = vbno then
   exit sub   
                End If               

                'make script sleep
                WScript.Sleep(intMinsToWait * 60 * 1000) 

  strShutdown = "shutdown -s -t 0 -f -m \\" & "."
  objShell.Run strShutdown
  Wscript.Quit
End sub

Tuesday 3 April 2012

QTP .net .object properties - getting started

I've been using the .net native properties to provide extra support for the controls in the application I'm working on.

An area I've been struggling with is to identify what type of .net control I'm working with. Qtp may recognise it as a swflist but the .net control type could be completely different to another type of swflist. I've found a simple way to determine the control type:

object.ToString

So if you wrap this around a msgbox box you can find out the .net obejct class you're working with:

msgbox swfwindow("a").swflist("abc").object.ToString

When I executed this against a control I have, it tells me that it's a system.windows.forms.checkedlistbox type. Perfect, I can now go and look up the methods for this in the MSDN Library.

Wednesday 21 March 2012

QTP Object Synchronisation

Performing synchronisation in scripts is something that should be straight forward - the .exist(0) method makes this extremely simple. However, things start to get messy if we need to program extra error handling around it - something that should be straight forwards ends up being several lines of code. If you repeat this several times over then it's not being very efficient.

A way around this is to call a generic synchronise routine each time you want to wait for an object to exist. By following this practice you can build custom error handling into the synchronise routine without bloating your main automation code. I've built this into my keyword framework so that I always call it before trying to interact with an object, therefore I know that the object must exist before I try to do anything with it. It keeps my code clean and improves the reliability of my scripts.

A basic version of this is below, you can call it by hard coding the maximum wait value or if you want to abstract it further, define the timeout value in an environment variable.

The intMaxWait variable is the time in seconds you are willing to wait. The function returns a true or false value depending on whether or not the object was found.

How to call it:
blnSynced = SynchroniseOnObject(Browser("application a").Page("page b").WebElement("element c"), Environment("CustomTimeout"))

Function Code:
Function SynchroniseOnObject(objRepositoryItem, intMaxWait) 
 Dim blnObjectFound : blnObjectFound = False  
 Dim dtStart, intWaitedTime 
 Dim blnBreak : blnBreak = false  

 'get the current start time 
 dtStart = now()   
 do  
  'check if the object exists  
  If objRepositoryItem.Exist(0) Then   
   blnObjectFound = true   
   blnBreak = true  
  Else   
   'otherwise loop - calculate how long we have waited so far   
   intWaitedTime = datediff("s",dtStart,now())   
   If intWaitedTime  > intMaxWait then 
    blnBreak = true  
   else 
    'put some code in here to trap any errors,popups etc
   End if   
  End if
 loop until blnBreak  
 
 SynchroniseOnObject = blnObjectFound
End Function

Thursday 15 March 2012

QTP: How to post back to the Quality Center Execution Grid during test execution

One of the issues we face is that when our QTP tests fail is that we need to load up the QTP results to see what's happened. If a group of tests have all failed at the same point then usually it's due to the same reasons (typically environmental or a data error). If we can post some of this information back to the QC Exeuction Grid then it potentially saves us time from opening all of the results.

The code below will show you how to do this from within QTP. There are a few things to bear in mind:
  1. Our framework uses a custom reporting function through which all pass, fails, warnings etc are passed. Therefore it allows us to single out errors and post them back to QC.
  2. The QC field has a limitation of 255 chars, so we use this only to post the last error. Our QC field is called "Error Details".
  3. It's used to give us a quick snapshot as to the cause of the failure so we can determine to rerun or investigate the result further. This isn't intended to replace results analysis - we don't "pass" tests until we've fully investigated the results.

The code:
Sub WriteToQCExecutionGrid(strValue)
 Const QCFieldName = "Error Details" 'change this to match your QC field name
 Dim strThisTestName
 Dim objTSTestFactory
 Dim objField
 Dim objTestList, objTest
 Dim blnTestFound : blnTestFound = false
 Dim strInternalFieldName 
 Dim objQCTestInstance

 'setup default values
 strInternalFieldName = ""

 'See if we're connected to QC 
 If not qcutil.IsConnected Then 
  exit sub
 End if

 'See it we're connected to a test lab test set
 If (qcutil.CurrentTestSet is nothing) Then 
  exit sub
 end if

 'Connect to the ts test factory - this represents the execution grid
 Set objTSTestFactory = qcutil.CurrentTestSet.TSTestFactory
 'Now search through the fields to see if we can find one called Error Details"
 For each objField in objTSTestFactory.Fields
  If (objField.Property.UserLabel = QCFieldName) then 
   strInternalFieldName = objField.Name
   Exit for
  End if
 Next

 'If we didn't find the field name, exit
 If strInternalFieldName = "" Then exit sub

 strThisTestName = qcutil.CurrentTestSetTest.Name

 'Now find this test in the execution grid
 Set objTestList = objTSTestFactory.NewList("")
 For Each objTest In objTestList   
  If (objTest.Name = strThisTestName) Then
   'Test was found, so update flag and exit
   Set objQCTestInstance = objTest
   blnTestFound = true
   Exit for
  End If
 Next 

 If not blnTestFound Then 
  exit sub
 End If

 'objQCTestInstance will now hold the test that we need to update
 objQCTestInstance.Field(gstrInternalFieldName) = strValue
 objQCTestInstance.post
End Sub

Example:

Call WriteToQCExecutionGrid("Failed to Load Webpage")

Tuesday 14 February 2012

QTP: Getting the properties of childobjects

I've been doing a lot of work with childobjects, and one of the issues I've faced when working with the returned collection is determining what runtime properties each object in the collection has. Once you know what properties the object has, it makes it easier to filter your childobject descriptions or choose the right object in the collection to work with.

Unfortunately QTP has lousy debug capabilities in this area so I started researching ways in which you can display the properties of QTP objects and I came across this useful post.

I tweaked this a little bit to produce a function that prints out all of the object properties to the debug window. When combined with some code to iterate the childobjects you can quickly see what all the properties are. This has been tested with QTP 11.

The code

First of all, here's the function to print out all of the object properties

Sub PrintObjectProperties(objQTPObject)
  'This article helped with this function:
  'http://motevich.blogspot.com/2008/11/qtp-object-indentification-properties.html
 Const HKEY_LOCAL_MACHINE = &H80000002
 Dim objReg, strKeyPath
 Dim arrObjectProperties, i
 

 Set objReg = GetObject("winmgmts:{impersonationLevel=impersonate}!\\.\root\default:StdRegProv")
 strKeyPath = "SOFTWARE\Mercury Interactive\QuickTest Professional\MicTest\Test Objects\" & objQTPObject.GetROProperty("micclass") & "\Properties"
 objReg.EnumValues HKEY_LOCAL_MACHINE, strKeyPath, arrObjectProperties

 'now we've got an array of the properties, output all of the runtime properties
 Print objQTPObject.ToString
 Print "micclass:" & objQTPObject.GetROProperty("micclass")  

 If IsNull(arrObjectProperties) Then
  Print "** Object RO Properties could not be found for this class**"
 Else
  For i = 0 to UBound(arrObjectProperties)
   Print arrObjectProperties(i) & ":" & objQTPObject.GetROProperty(arrObjectProperties(i)) 
  Next
 End If
 
End Sub

Then combine with some code that retrieves a collection of childobjects from a QTP object:

Dim objDesc,objDescCol
Dim objQTPObject
Dim n

Set objQTPObject = Browser("A").Page("B")
Set objDesc = Description.Create
'Add any other description filters here
Set objDescCol = objQTPObject.ChildObjects(objDesc)

If objDescCol.count > 0 Then
 For n = 0 to (objDescCol.count-1)
    Print "Child Object " & n
    Call PrintObjectProperties(objDescCol(n))
    Print ""
  Next
End If

The result is that all of the properties of the children are output to the print window.

Thursday 9 February 2012

SwfListView: Determining the image in a listview using QTP

Some applications use an image in a listview to display information about the status of the item. Unfortunately, QTP does not provide any methods to access this information using GetROProperty.

By using the .object property we can get access to the exposed .net properties and methods of the listview.

Take the following screenshot:





The application is using a red and green icon to display the status. What we want to do is know which icon is being displayed. The following code will output this information to the debug window:

Set oItems = SwfWindow("Application A").SwfListView("lvwListView").Object.Items

For n = 0 to oItems.count -1
 print "Item index " & n & ", ImageKey=" & oItems.Item(n).ImageKey
 print "Item index " & n & ", ImageIndex=" & oItems.Item(n).ImageIndex
Next
Note that we are using two properties here - imagekey and imageindex. This is because the development team may implement this image in two different ways, so one of these pieces of information will tell you what you need to know.





In my example, ImageIndex is set to 1 when the green icon is shown.

For more information and to see what other properties/methods are available, see the ListView and ListViewItem msdn documentation.

Tuesday 7 February 2012

Executing Coded UI Tests from Quality Center

A few months ago I was exploring the features and practicalities of using Coded UI for automation. One consideration to take into account was whether or not coded UI tests could be executed from Quality Center. 

First of all, why would you want to do this? Well, the QA process at the client site was all geared around Quality Center - test cases, metrics, defects etc so to go down a pure CodedUI route would mean switching all tooling and processes over to TFS. If you've worked in large organisations you'll appreciate that's a fairly long roadmap! 

An iterim solution to was to bridge this gap by seeing if we could execute CodedUI tests from Quality Center.

In this article I will show you the proof of concept I managed to implement. We never went down the CUI route so I haven't been able to spend any more time refining this, but it will get you off the ground and help you explore the possibilities around this integration. This article assumes you have some experience using Visual Studio and Coded UI.

The goals

In order for this to work, the following criteria needs to be supported by the solution:
  1. CUI Test Cases must be represented in Quality Center.
  2. CUI Test Cases must be launched by the automatic runner featur
  3. The execution status of the test must be captured.
  4. A meaningful execution report / logfile must be attached to the results to support analysis.

The limitations

There were a couple of design factors to take into account:
  1. Our CUI tests were designed on a 1 to 1 basis, i.e 1 CUI test represented 1 test case. Multiple iterations were not factored into the design. If your CUI test does use multiple iterations, then you will need to adjust the results analysis function to determine the overall run status for the test. There may also be some clever ways to change the command line execution to run specific iterations.
  2. A QC remote agent feature is not supported so an instance of Quality Center needs to be run on each machine where you are executing the test. I suggest using the standalone QC client exe (the one that doesn't use IE) so that CUI doesn't get confused with the QC browser.

Implementation

To make this article easier to digest, I've split it up into 4 main stages:
  1. Installing mstest.exe on the client machines.
  2. Compiling the CUI test and placing the dll on the client machine.
  3. Creating a VAPI-XP test in Quality Center
  4. Executing the CUI test and analysing the results

1 - Installing mstest.exe on the client machines

mstest.exe is required to execute the coded ui tests, it's a command line tool which you use to launch the tests. We will invoke mstest.exe from Quality Center and tell it which coded ui test to execute.
You will need to install the agent as an interactive process. This Microsoft Article explains more. If you don't have the CD it looks like you can download the agent here.

2 - Compiling the CUI test and placing the dll on the client machine

The next step is to compile your tests and save them onto the client machine. The basics of how to do this are as follows:
  1. Load your CUI project in Visual Studio.
  2. Right click the project and select “build”.
  3. In the output window, note the directory that the dll is compiled to.
  4. Copy the entire directory to the the host machine where you will be executing the test on(ensure you place it on the C: drive of the host machine, this may have been a system policy in place where I worked but I couldn't launch the tests from any other drive).
Note: If you plan to run a test on a machine where Visual Studio is not installed, you will need to compile it with all of the supporting dll's copied into the local folder, as per the screenshot below:



3 - Create a VAPI-XP test in the Quality Center test plan

You will need to create a test in Quality Center that represents the CUI Test. This is so that you can run it from the test lab. To do this, create a VAPI-XP test and then write some vbscript code that calls the CUI test:

1) Navigate to the QC Test Plan
2) Select "Tests" > "New Test".
    - Type: VAPI-XP Test
    - Name: Give it a relavent name
    - Click OK







3) Choose the Script Language as "VBScript" then click Next,

















4) Set the test type to "Console Application Test" then click Next.





















5) Click on Finish.
6) Select the test and click on the "Test Script" tab.
















7) Replace the vbscript code with this code.
    8) Now you will need to modify the code. The following lines of code will need to be changed in accordance with your setup:
    • Dim strPathMSTestExe :   strPathMSTestExe = "<path to where mstest.exe is installed>"
    • Dim strTestContainerDLL : strTestContainerDLL = "<path to the compiled test dll>"
    • Dim strResultsDirectory : strResultsDirectory = "<path to a directory where the results can be saved>"
    • Dim strTestName : strTestName = "<the name of the coded ui test to run>"  
    Once you've done this, the test is ready to execute from the test lab.


    4 - Executing the CUI Test and Analysing the Results

    To execute the test, simply add it to a Quality Center test set and run it locally (I'm assuming that having come this far you can manage this!).

    Remember the limitation we have is that it can only be run locally (unless you want to program some sort of agent yourself). Therefore I recommend running QC from the standalone client so that the CUI test doesn't get confused with any QC IE sessions that may be open.

    I have programmed a few functions to analyse the results (the .trx file), retrieve any attachments and upload them to the QC. These are attached to each individual run .The functions performing this are called:
    • GetResultsXMLStatus
    • AttachFileToResults
    • GetArrayOfAttachments
    These can be found at the bottom of the vbscript code.

    Note: This method is based on our setup that one test equals one iteration. If you run multiple iterations within a single test then you may need to implement your own way of analysing the results.






















    Closing Comments

    This article demonstrates a proof of concept that CUI tests can be executed through Quality Center. Unfortunately I'm no longer engaged on a project using CodedUI so I haven't been able to take this further.

    I welcome any comments, useful suggestions or enhancements you've been able to make. Hopefully this should give you enough help to get off the ground!

    Monday 30 January 2012

    QTP Notify: a simple utility designed to display notifications in the system tray

    QTP Notify is a simple utility designed to display notifications in the system tray. The purpose of it is to display useful information when a test is executing in way that does not take focus away from the application under test. This can be useful for debugging tests or understanding how far through execution a test is without interfering with the desktop.

    
    Add caption
    





    The download contains 3 files:
    • The application (QTPNotifier.exe)
    • The QTP function that calls the notification (RaiseNotification), and
    • An Example QTP 11 test.

    Setting Up

    To setup and integrate this with your tests:
    1. Copy the QTPNotifier.exe application to your computer or network drive.
    2. Include the RaiseNotification function in your framework.
    3. Modify the "NPATH" constant in the function so that it points towards the location where you saved QTPNotifier.exe.
    4. In your test scripts call the RaiseNotification function, an example would be: Call RaiseNotification("Title Test","Message Test")
    How it works:

    The application polls the directory where it's saved to and looks for a text file with the format "<computername>notify.txt". When it finds the file, it reads the first line as the title and the second line as the message. It deletes the file and then displays a notification bubble in the notification area. The QTPNotifier.exe application will stay alive for another 60 seconds and repeat if another message is found. If no notification has been raised in the last 60 seconds the application will naturally terminate.

    Things to note:
    1. Theres a small overhead each time you call this whilst the function searches for running processes (might be a second on some machines). This is useful as the QTP Notify application needs messages to have at least 100 milliseconds between each notification.
    2. Sometimes the notification appears behind the current window - this is a known issue with Windows XP, usually a reboot fixes this.
    3. If you are relying on low level mouse moves and clicks then you might click the notification bubble by accident if the mouse is clicked in the bottom right area. You could modify the function to disable itself if a certain environment variable is set.
    4. It's not designed to be robust or perfect...but it does the job for me!