|Cogitek RIATest 6 Documentation||Copyright © Cogitek Inc.|
One of the most frequently arising issues when writing automated tests is synchronization. When a test script runs it emulates user actions on Application Under Test. However those actions cannot be performed at an arbitrary speed because it takes time for the application to execute and react to the actions. This naturally leads to the requirement for the automation tool to wait when needed for the preceding actions to complete their execution before emulating the next action or checking for a verification condition. This is called synchronization.
RIATest is capable of synchronizing automatically in the following cases:
However there are times when the automatic synchronization capability of RIATest is not sufficient and you have to manually specify that the script execution must wait for a specific condition. This usually happens when an emulated user action triggers an asynchronous operation in your application. For example clicking on a button starts an operation that connects to the server, requests some data and when it is received populates it in a list or data grid. Network operations are all inherently asynchronous because they take time and their duration is not definite. RIATest does not know that an asynchronous operation is happening in your application and thus will immediately attempt to execute the following action or property verification operation. If this next operation is for example an attempt to select an item from the list (to be populated) it will most certainly fail since the network operation has not yet completed, data is not received, the list is not populated and the item to be selected does not yet exist.
So, how do you make your script wait in such cases? The general idea is the following. Try to imagine what the user does when working with the application. The user clicks on the button then starts watching the list. When items appear in the list only then will the user attempt to select from the list. In other words the user is synchronizing on a visual clue. The clue here is the list itself. While it is empty, wait. When the items appear, go ahead and click. Your goal as script author is to try to emulate the behaviour of the user. Let's see how our script would look if we record user actions (click on the button and make selection from the list).
FlexButton("Load")=>click(); FlexList("MyList")=>select("The Item");
What we need is to make the script wait after the first action for the list to be populated before proceeding to the second action. One of the ways to do it is to use the numAutomationChildren property of the list. This property is the number of the visible items in the list. If the list is empty this is property equals 0. So what we could do is to wait until this property's value becomes greater than zero, e.g.:
FlexButton("Load")=>click(); waitfor(FlexList("MyList")=>numAutomationChildren > 0); FlexList("MyList")=>select("The Item");
Here we are using the waitfor operator which keeps querying expression in the parentheses until it becomes true (or until timeout - by default 30 seconds). So line 2 above would wait until at least one item appears in the list. In trivial cases this would be sufficient. The data from the server is received in one big portion and all items in the list are populated at once. So as soon as we make sure there is at least one item in the list we can be sure all them are there and we can make the selection. However for more complex cases this may not be enough.
Suppose that the loading process occurs in portions, i.e. multiple requests are made to the server sequentially and each reply from the server returns a small portion of data which the application immediately shows in the list and then sends another request to the server and so on until all the data is received and is populated in the list. If this was the case then waiting for at least one item to appear in the list does not guarantee that the particular item we want to select is also present and can be selected. The waitfor operation in the above code snippet would not sufficient in such a case.
What we need to do is to wait until the particular list item appears in the list so that it can be selected. The following code does exactly that:
FlexButton("Load")=>click(); waitfor(FlexList("MyList")->FlexListLabel("The Item")=>visible); FlexList("MyList")=>select("The Item");
The second line will wait until there is a visible item with text "The Item" in the "MyList". This does not guarantee that the entire list is loaded but it does guarantee that the item that we want to select is available. Does this pose a problem? It depends on your application. If it is ok for the user to make a selection when the list is partially loaded and is still loading then all will be fine. However, if it is not ok to make such a selection then the application should prevent the user from making such a selection in the first place. It does this by providing another visual clue to the user. For example it would show a progress bar while the operation is in progress and most likely would disable the list control. But these visual clues can be successfully used for synchronization from the script. For example if your application disables the list while loading and enables it when the load is complete you could do the following:
FlexButton("Load")=>click(); waitfor(FlexList("MyList")=>enabled); FlexList("MyList")=>select("The Item");
Line 2 in above code will wait until the entire list is loaded and the list is enabled before proceeding to the select action.
We have presented a few alternate ways to perform synchronization in this article. What they have in common is the need to understand what visual clues are presented by the application to the user and use these visual clues for synchronization. In most cases this approach can be successfully used to perform the necessary synchronization.