One of the issues my team has been encountering writing automated tests on iOS 7 with Rufus is the time it takes for Selenium::WebDriver to get a response back from the Appium server when running automated tests using cucumber.

Part of the slowness could be that we’re starting the Appium server using the slower, but more consistent native instruments library option.

It seemed that the selenium.find_element call took awhile to return a result, and that the selenium.page_source call returned a result more quickly. The first step in optimizing rufus to run faster was to parse the result of the page_source call for useful information. This wouldn’t work in every case, but if we needed to ask selenium to find an element only to grab one of its read-only properties (such as whether or not the element was visible), it might be faster to just ask selenium to dump out the page data and we’ll take it from there.

There are several cases where we can get valuable information about the state of a screen element simply by looking at the page source data. We tabbed “read-only” data as whether or not an element is enabled? or displayed?, whether it exists? or if it had text and, if so, what was its value. We could also use the page source to determine the elements that are in a tableview and whether or not those elements are sorted, but that is not implemented yet. Below is a small sample of what the selenium.page source might return:

 "{\"name\":\"showAlertButton\",\"type\":\"UIAButton\",\"label\":\"showAlertButton\",\"value\":null,\"rect\":{\"origin\":{\"x\":304,\"y\":302},\"size\":{\"width\":150,\"height\":30}},\"dom\":null,\"enabled\":true,\"valid\":true,\"visible\":true,\"children\":[{\"name\":\"firstChild\",\"type\":\"UIAImage\",\"label\":\"showAlertButton\",\"value\":null,\"rect\":{\"origin\":{\"x\":0,\"y\":1023},\"size\":{\"width\":768,\"height\":1}},\"dom\":null,\"enabled\":true,\"valid\":true,\"visible\":true,\"children\":[{\"name\":\"firstNested\",\"type\":\"UIAStaticText\",\"label\":\"showAlertButton\",\"value\":null,\"rect\":{\"origin\":{\"x\":0,\"y\":1024},\"size\":{\"width\":668,\"height\":44}},\"dom\":null,\"enabled\":true,\"valid\":true,\"visible\":true,\"children\":[]}]},{\"name\":\"secondChild\",\"type\":\"UIAButton\",\"label\":\"showAlertButton\",\"value\":null,\"rect\":{\"origin\":{\"x\":0,\"y\":1024},\"size\":{\"width\":568,\"height\":34}},\"dom\":null,\"enabled\":true,\"valid\":true,\"visible\":true,\"children\":[{\"name\":\"childOfSecondChild\",\"type\":\"UIATableViewCell\",\"label\":\"showAlertButton\",\"value\":null,\"rect\":{\"origin\":{\"x\":0,\"y\":1024},\"size\":{\"width\":468,\"height\":24}},\"dom\":null,\"enabled\":true,\"valid\":true,\"visible\":true,\"children\":[]}]}]}"

In reality the selenium.page_source call outputs significantly more json data, but it’s clear we can parse this data into a hash that would easily produce valuable information we could use.

For instance, the name of the first element is the ‘showAlertButton’ and it is of type UIAButton and this button is enabled because its enabled property is set to true (\“enabled\”:true). A typical automated test might be to tell if a button becomes enabled after a user enters a value into a text field. Using rufus, one might have set up a page object called SignUpPage that defines a button accessor like so (More on page object):

button(sign_up_button, :name => 'sign up')

Then through the page object pattern a we have a way of determining at a given point in time whether or not the button is enabled or disabled.

on(SignUpPage).sign_up_button_view.enabled?

Here we’re calling the sign_up accessor and through the page object we automatically get a method #{element_name}_view which returns the view element in question. Then we are calling enabled? on that return value.

Under the covers rufus is calling selenium.find_element(:name, ‘sign up’) to return an object of type Selenium::WebDriver::Element, which we can then inspect. Before we decided to look into optimization strategies this call would make one call to find the element and then another call to check if that particular element was enabled?

With our optimization strategy rufus no longer calls selenium.find_element in this situation. That is because the property that we’re interested in can be read from the page source data.

Now under the covers rufus does this:

json_data = selenium.page_source  #returns json
enabled?(json_data, 'sign up')  #parses json

Rufus takes the resulting json from the selenium.page_source call then parses it, looking for the element named ‘sign up’. This call to find the element strips all of the unnecessary json data, leaving only the key/value pairs pertinent to the view named ‘sign up’. Then it asks for the value of the “enabled” key. All this parsing seems to happen faster than asking selenium for the element and then asking the element if it is enabled.

I ran a short set of scenarios for the CoPilot app at Northwoods and here were the results based on this simple change.

optimized: false
4 scenarios (4 passed)
8 steps (8 passed)
9m50.903s

optimized: true
4 scenarios (4 passed)
8 steps (8 passed)
4m53.679s