Mobile devices are becoming important platform for customers and it plays crucial role when it comes for organisation’s digital strategy. And when it’s mobile, there are bunch of variations present; iOS, android, iPad, windows, blackberry, mobile web and so on! With such rapid development going on around these many platforms, we need to have refined end to end high level functional tests for these number of platforms. If such is a case, we would like to have some mechanism in place where we write once and get free for others, unlike Java. The blog post talks about the exact problem and solution we came up with!

Cross platform tests

“Cross platform” refers to idea that means, same test(scenario) can be executed on multiple platforms. This does not have anything to do with your app is pure native/hybrid or combination of both.
One can decide to go for writing cross platform tests, if:

  1. Similar functionality is present across the different platforms.
  2. Test suite is covering high level end to end flows. (Minimal number of tests are present at UI layer.)

Cross Platform automation tests

Cross Platform automation tests

When any automation suite we go for writing, we have following expectation and cross platform automation is not an exception for this.

  1. It should take very minimal time to add any new tests.
  2. It should be very easy accommodate any new changes and hence maintenance.
  3. It should be very simple and allow us to reuse the code.

Well, Page Object Pattern is natural fit for any application which has workflow and it satisfies above expectations if used in proper way. But how can we have one test which will work for all platforms? And well, we are not the first one to face this problem. People have already faced this problem and came up with solutions like:

  1. Cross platform best practices by Calabash
  2. Strategy pattern
  3. Screen object pattern

These are some of the ways people have tried to write their automation suite. But if we look at all these examples, scenario and step definition(in context with Cucumber features) are common and rest of the part is specific to each platform implementation.

Different pages for different platform

Different Pages for different platform

The problem with this approaches is, we are not able to reuse the code as much as possible. Whenever any new test we need to implement, we will need to duplicate same service for respective page object and thus, violating DRY.

Surely, we saw problem with this approach and the root cause of this was different pages being used for different platforms. Solution we came up with is, “One page to test them all!”

Same page for different platform

One Page for all platforms!

The challenge with this approach was, how we can have one page being all different platforms and their behavior is different. We solved these challenges in an iterative way taking one challenge at a time. If we consider android, iOS and mobile web platforms, following are the challenges:

  1. Locating UI elements mechanism is different
    As each platform eco-system is different itself, mechanism for identifying each locator is different.

  2. Different UI interaction
    UI gesture for each platform is different and so does an API varies provided for same ui interaction.

  3. Different Automation Tools
    Currently there is no single tool which can be used for all platforms.

  4. Different UI navigation patterns
    Each platform recommends some style guide and UI varies from platform to platform.

We solved above problems one by one and keeping basic automation expectations in mind.

  • Locators for each platform
    It’s just way of specifying for each platform is different., where I see platform as key and locator as it’s value. Simply as follows, if in Ruby:
       web: "#Food Items:"
       droid: "* id:'FoodItems'"
       ios: "* title:'Food Items'"
  • Different UI interaction
    Platform specific behaviour will change for each of the ui component on the device. Entering text for textbox will vary for the platform. Well, again it’s just way of specifying the element on the page for different platforms. We have solved specifying locator for each platform already; all we need is an abstraction for each of the UI component.
          t = ({
                               web: "#username",
                               droid: "* id:'username'",
                               ios: "* id:'user_name'"

Eventually there will be other elements like checkbox, button, table, dialog and so on. We can define different behavior as per each component and common behavior for all this can be extracted out to element class.

Same page for different platform

Element abstraction

  • Different automation tools
    Currently there is no single API which exposes a common API for all these platforms. Although the name seems similar on each of the API, underneath actions are different. This can be solved by defining a module which will do all UI actions and whose responsibility will be defining a common interface for different platform.

With this design in place, our page objects will be platform agnostic and any change in the corresponding API upgrade will not affect the behavior of page objects.

Same page for different platform

Driver - All UI actions will be delegated to platform specific driver

  • Different UI Navigation patterns
    A classic example of this being Navigation drawer on android, tab bar on ios and menu bar on web. This can be solved by defining abstraction for menu representation and delegating UI actions to common API module. We can have menu as page and menuItems can be a special element type. You can look at the implementation here.

So even if UI elements are different and UI gestures are different, we can define a similar component as an abstraction which will represent all the platform. For example,

  1. Tab bar layout
    We can model this in different ways, either we can have each tab as a page and base page will have functionality to navigate to each of the tab or it can be special type of element, where click on each tab will be a transition element.

  2. Dialog
    There are different types of dialog: alert, confirmation, prompt etc. If there are standard dialogs and the behavior is consistent, we can define dialog as element, where any user action on that dialog will be different transition. If dialogs are doing more than just being information, then we can model dialog as different page itself.

  • Abstracting wait and transition

    We saw that set of actions are getting repeated when we are navigating from one page to another page.

    1. Click on button.
    2. Wait for next page to load.
    3. Return the next page when loading is complete.

    And whenever we repeat, there is an opportunity of abstracting it. Here, all we need to do is identify the correct class, where we can have this responsibility. We definitely can’t put it in driver as it is responsible for interaction. If we go one level higher, element seem to be correct place for this. Again every element click action does not lead to transition and what we can have in this case is another element type, Transition Aware element!

    This element in addition to locator will have knowledge of transition it can lead to. Transition can be of two type, success and error and each can very well have multiple transition in itself. Considering all these requirements, we can specify like this:

          @button =
               :web   => '#continue_button',
               :ios   => "* marked:'Order'",
               :droid => "* marked:'Order Food'"

              :to =>    [ UserDetails, PurchaseSummary]   # represent success transition
              :error => [ UserDetailsErrorDialog ] 

One page for all in action
Here we go with one page for all platform, end to end implementation will look like:

Same page for different platform

All glued together!

Accommodating change in Functional test

We have solved all the concerns we had when we started to think about one page for all platforms. Let’s see how it’s easy to incorporate any change in this approach. In any case, at functional UI layer test, your automation should change only in two of the cases:

  1. Any change any UI elements. (Element locator change)
  2. Any change in the existing flow. (Transition change)

“If we want to accommodate these changes in our current framework, it’s a change at single place for all the platforms!”

  1. Github project
    For demonstration of automation framework, created sample Food app for all platforms, you can find those here. Refer Automation framework for this food app here.

  2. Talk
    We have given talk at multiple places, find the talk here and similar experience summary for very first talk.

  3. Slides
    I presented at Selenium conference on the very similar topic. Find slides here.

Aroj and I paired to build this framework for cross platforms.