Tuesday 15 January 2013

Using Excel to Build QTP Tests and Test Sets in Quality Center

This post will show how you can use Excel VBA to automatically create a QTP Test in Quality Center and then automatically assign the test to a test set. I have used this in one of my frameworks to represent Excel keyword driven test cases with QTP driver tests in QC, and automatically build test labs linking to the tests. Essentially it is automating the QC test plan and test lab builds.

Firstly I would like to put a few caveats in place:
  • This is just a proof of concept release. Please feel free to take the spreadsheet and develop it for your needs but I won't be able to support it for you. If you get stuck, start debugging the VBA and looking at the OTA documentation.
  • I have written functions to encapsulate Quality Center OTA calls. For some reason these can be a big buggy, so if the OTA calls fail you usually need to close Excel completely and then reopen it.
  • If you're testing this out remember to delete the tests & test sets created between each run, otherwise the code will throw some warnings.

The spreadsheet is a two step process:
  • It copies a known QTP test to a location in the test plan and then renames it. This creates a unique version of the test.
  • Then it creates the test lab and associates the QTP test to it.

Here's how to do it:
  • First of all download the spreadsheet from this link and open it in Excel (ensure macros are enabled). 
  • In Excel open up the VBA editor (Alt + F11) 
  • Open the module "modBuilder" and find the function called "Builder".
  • You will need to edit the line and change these values with your own QC setup:
If Not ConnectToQC("YOURSERVERNAME", "YOURDOMAIN", "PROJECTNAME", "USERNAME", "PASSWORD") Then
  • Return to the Excel workbook. Each row represents a test that will be created and added to a test set. You need to ensure that:
    • The template path and template name have been defined (columns A and B). These will point the code at an existing QTP test in the test plan.
    • Define the location where the new test will be created (columns C and D).
    • Define the test lab path and test set name where the new test will be created (columns E and F).
Once this is all setup click on the "Build" button and your tests and labs will be built!



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.