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")