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:
‘.Action Code
If CritialCondition = False Then ExitAction
‘.Continue Action
But this is uninformative. So we add a reporter command:
If CritialCondition = False Then
Reporter.ReportEvent MicFail, "Something terrible has happened", "Aborting"
ExitAction
End If
And we probably got some objects to remove from memory:
If CritialCondition = False Then
Reporter.ReportEvent MicFail, "Something terrible has happened", "Aborting"
oFile.Close
Set oFile = Nothing
ExitAction
End If
============================================
Ho wait, we’ve gathered some data we need to report back:
If CritialCondition = False Then
Reporter.ReportEvent MicFail, "Something terrible has happened", "Aborting"
oFile.Close
Set oFile = Nothing
DataTable("out_EntityID", dtlocalsheet) = sEntityID
‘More here
ExitAction
End If
========================
Well, you probably get the picture. Pretty soon we get massive amounts of code in all the exit gates. This means we have duplicate code to maintain. Duplicate code is the digital manifestation of pure evil – and it’s never a good idea to have pure evil in you actions. Seriously though, this is exactly the kind of things that produces untraceable bugs, and it must be avoided at all cost.
So, how can we deal with this situation? One way is to create an exit gate function. It’s the ONLY function I ever put within an action, and not in an external file. Here’s an example of my action template, with the function:
Dim sResult ‘I store result data and values to be reported up the
‘action-call chain
Dim sActionReport ‘Instead of flooding the log with inner-action messages,
‘I store them, and report all of them at the exit gate.
‘Errors are still reported on-the-fly
‘Action code goes here
‘All the exit gates execute only one command : ActionEnd.
‘It receives two parameters: Boolean for Pass/Fail,
’string for the exit reason
If CriticalCondition = False Then Call ActionEnd(False, "Reason for exiting")
‘Rest of action
‘Even the normal successful action exit is managed through ActionEnd,
’so the last line in every action is:
Call ActionEnd(True, "Action successful")
Sub ActionEnd(bStatus, sReason)
‘Report details
Reporter.ReportEvent MicGeneral, "Inner Action Logs", sActionReport
If bStatus = True Then
Reporter.ReportEvent MicFail, "An error has occurred", sReason
Else
Reporter.ReportEvent MicPass, "Action successful", "See inner logs"
End if
‘Plant datatable info for action-call chain
DataTable("out_Status", dtlocalsheet) = bStatus
DataTable("out_Result", dtlocalsheet) = sResult
‘More if needed
‘Close objects and set to nothing here
‘Other needed exit code
ExitActionIteration
End Sub
With this mechanism, maintaining the exit code becomes very simple, and the logs are much more readable. Ok, so this solves the problem for actions, but what about functions? Well, obviously we can write an inner function within a function, but there is an alternative solution. It’s less elegant than the ActionEnd solution by far, and is harder to maintain, so I recommend using it only in a small number of very complex functions.
The solution is based on the Execute command, so I recommend reading about it in QTP’s help file in case you’re not familiar with it. In a nutshell, the Execute command takes a string, and runs its contents as if it were VBScript code. So for instance the command Execute "msgbox(2)" will pop a message box with the number 2. Here’s an example for the solution, applied to the ComplexFunc function:
Function ComplexFunc
Dim sExitCode
Dim sResult
Dim oFile ‘will be FSO textstream
’separate code lines by vbcrlf or ":"
sExitCode = "oFile.Close" & vbcrlf & _
"Set oFile = Nothing" & vbcrlf & _
"ComplexFunc = sResult"
‘More exit code
‘Function code goes here
‘Exit Gate
If CriticalCondition = False then
sResult = "False, No Connection"
Execute sExitCode
Exit Function
End if
‘More function code
‘Successful exit
Execute sExitCode
End Function