Peeling an Onion Code
Peeling an Onion Code
In my previous post I presented code structure looking like layers of an onion. In this post I’m exploring ways to transform it to structures that have much less drawback while performing same (or richer) functionalities.
Let’s begin from the very first issue: readability of an Onion Code.
Generally speaking, test steps are sequential. Even if they depend on each other they don’t have to be wrapped around each other. The following code demonstrates how all the layers could be rewritten in a step-by-step manner.
As you will see our code is not a “matryoshka” anymore.
Do While True
If Not Browser("title:=Google.*").Page("title:=Google.*").Exist Then
boolStatus = False
sMessage = "Failed to find Google Search Page"
Exit Do
End If
If Not Browser("title:=Google.*").Page("title:=Google.*").WebEdit("name:=q").Exist Then
boolStatus = False
sMessage = "Failed to find input box on Google Search Page"
Exit Do
End If
If Not Browser("title:=Google.*").Page("title:=Google.*").WebEdit("name:=q").CheckProperty("disabled", 0) Then
boolStatus = False
sMessage = "Input box is disabled; couldn't type search query"
Exit Do
End If
Browser("title:=Google.*").Page("title:=Google.*").WebEdit("name:=q").Set "test"
If Not Browser("title:=Google.*").Page("title:=Google.*").WebButton("name:=Google Search").Exist Then
boolStatus = False
sMessage = "Failed to find Google Search button"
Exit Do
End If
Browser("title:=Google.*").Page("title:=Google.*").WebButton("name:=Google Search").Click
boolStatus = True
Exit Do
Loop
If boolStatus Then
Reporter.ReportEvent micPass, "Google Search", "Search query submitted"
Else
Reporter.ReportEvent micFail, "Google Search", sMessage
End If
The code above performs a couple of single atomic test steps. If written as manual testing script, they would look someting like the table below.
| Object | Action | Expected result |
| Input box | Type in “test” | Entered text appeared |
| “Google Search” button | Mouse click, single | The button was pressed |
Note, while testing manually we would perform much more than that. And the role of test automation is to serve testing needs. Therefore, any test step implementation should implicitly include service functionalities that verify context, gather information about test object, and automatically log it. The following sequence lists functionalities applicable to GUI interaction test step.
- locate GUI context (web page, dialog form, window, etc.)
- locate GUI object
- Input to or Retrieve information from GUI object
[log details if failed to do that]
[log details if failed to do that]
[log details if failed to do that]
[log details if successfully did that]
Note that steps 2,3 are generic and do not depend on test logic or business domain.
Let’s rewrite a test step now in accordance to the sequence defined.
Public Function Button_Input(ByRef objButton, ByVal sStep)
If Not objButton.Exist Then
Reporter.ReportEvent micFail, sStep, "Failed to find the button"
Button_Input = False
Exit Function
End If
If Not objButton.CheckProperty("disabled", 0) Then
Reporter.ReportEvent micFail, sStep, "The button is disabled"
Button_Input = False
Exit Function
End If
objButton.Click
Reporter.ReportEvent micDone, sStep, "Pressed the button"
Button_Input = True
End Function
A side note. If you’re using these code examples for your practical work you may want to consider getting familiar with another approach to function wrapping first.
I skip here providing of source code of Set_Context and Edit_Input functions.
Now, when we have the all atomic test steps written as generic functions the original code block becomes much more compact.
boolStatus = True
Do While True
Set objGUIContext = Browser("title:=Google.*").Page("title:=Google.*")
boolStatus = Set_Context(objGUIContext, "Google Search")
If Not boolStatus Then Exit Do
'
boolRC = Edit_Input(objGUIContext.WebEdit("name:=q"), "Google Search", "test")
If Not boolRC Then boolStatus = False
'
boolRC = Button_Input(objGUIContext.WebEdit("name:=q"), "Google Search")
boolStatus = boolStatus AND boolRC
'
Exit Do
Loop
'
If boolStatus Then
Reporter.ReportEvent micPass, "Google Search", "Successfully executed test steps"
Else
Reporter.ReportEvent micFail, "Google Search", "Failed to execute test steps"
End If
As you can see the code is not intended to check any single static requirement although it automatically performs many verification steps along the way. The code is intended to “travel” a certain pathway in the application, and collect information about process and application status.
As much as passed manual testing only tells that no problems were noticed but does not guarantee they do not exist, the testing code only reports success (or failure) of steps performed not requirements verified.
Note that the code is not designed to proceed if a testing context is not available but we may still want to collect as much information as possible within it – even if some testing steps were failed. This will give more details for investigation afterwards.
Another advantage is that we can now keep test logic and generic functions separately. Maintenance debt is significantly reduced.
By the way, it doesn’t mean we reduced testing time. We saved time on checking and now can do more exploration.


One response to "Peeling an Onion Code"
found your site on del.icio.us today and really liked it…