Adapting Tests to Mobile Devices of Different Types

Applies to TestComplete 14.0, last modified on January 23, 2019

This topic describes several approaches to adapting mobile tests to mobile devices of different types.

Issues Caused by the Device Specifics

Sometimes, you may face a situation when a test recorded on one mobile device cannot be played back successfully on another mobile device. This may happen because most mobile devices are different. They have a different screen size, density, orientation, operating system version and so on. Below is a list of what may affect the playback of your test:

  • Flexible layouts - an application may rearrange and reposition its controls depending on the device orientation, screen size and density. A typical example is a different layout of an application on tablets and smartphones.

    A mobile application with a flexible layout
    A mobile application with a flexible layout

    On small screens, an application may split its user interface elements into several layouts (panels, pages, views, tabs and so on). Besides, an application may display or hide some of its controls depending on the layout. Therefore, you may need to perform different actions to reach the same control on a tablet and on a smartphone.

    Due to these differences, a test recorded on a smartphone can be played back on other smartphones, but it cannot be played back on tablets. And vice versa - a test created on a tablet will work on other tablets, but may fail on smartphones.

  • Different hierarchy of objects - Different versions of iOS or Android may have dissimilar object hierarchies. So, a test may not find the needed object on another version of the mobile operating system. For instance, iOS text editor controls may or may not have the scrollview object depending on the iOS version used.

  • Coordinate-based actions - In certain cases, TestComplete may record your actions over an application using absolute coordinates. For instance, an action can be recorded as control.Touch(155,50). Absolute coordinates are not reliable, since they depend on the screen resolution of the mobile device. So, in order to improve your test, just modify it: instead of coordinate-based actions use the control's extended methods and properties (like, SetText, wValue), or at least replace absolute coordinates with relative coordinates (Touch(control.Width / 2, control.Height - 10)).

Adapting a Test to Mobile Devices of Different Types

The main problem caused by the device specifics is that a test may not find the needed objects and controls on a device that has a different screen orientation, application layout, OS version and so on. Therefore, to create a universal test, you need to provide a reliable way of finding the "mismatching" objects and controls.

There are at least two ways to achieve this:

Adjusting Name Mapping

You can use name mapping to work around some device-specific issues. Your aim is to adjust the mapping criteria in order to uniquely identify the needed objects on any mobile device.

To achieve this, you can perform the following actions over the "mismatching" object:

  1. Check the object's mapping criteria that were selected automatically. Probably, you may choose a better set of identification properties (take a look at native iOS or Android properties). Correct the object's mapping criteria if needed. See, Basic Mapping Criteria and Basic Mapping Criteria.

  2. Enable the extended search criteria for the object.

    Text editor object with enabled Extended find option

    It makes TestComplete search for the object on all levels down the application’s object hierarchy. See, Extended Search.

  3. Move the desired object up the mapped objects' hierarchy to a stable parent object. A stable parent object is an object that does not change its position on different devices. See, ../../../testing-with/object-identification/name-mapping/about.html#Aliases.

  4. If suitable, use conditional mapping criteria or specify the required child objects.

  5. Change the object name in automated tests according to its new position in the object hierarchy.

You need to correct the mapping criteria of all the "mismatching" objects in the test in the same way.

If this approach is used, most of the job is done by the name mapping engine; the keyword tests or scripts are almost not changed.

We use this approach in the iOS tutorial to solve the issue of a different object hierarchy of text editors in different iOS versions. For a detailed description, refer to the Adjusting the Test for Running on Multiple Devices step of the tutorial.

Searching for the Needed Objects

Another way to locate "mismatching" objects and controls on different mobile devices is to search for them using special operations and methods. You can use any of the techniques described in the Searching for an Object topic.

Searching for Controls in Keyword Tests

To search for "mismatching" objects in keyword tests, use the Find Object operation. This operation allows you to search for an object by its property values and then call a method or property of the found object.

For instance, the following keyword test searches for calculator buttons using their captions and then touches the found controls.

Searching for controls in keyword tests
Searching for Controls in Scripts

To search for "mismatching" objects in scripts, use FindNNN or WaitNNN methods of a static parent object. A static parent object is an object that does not change its position on different devices.

For example, the following code uses the FindChild method to search for calculator buttons by their ViewIDs and then simulates a touch over the found controls.

JavaScript, JScript

function Test1()
{
  var i, ParentLayout, PropNames, PropValues;
  for(i = 0; i < Mobile.ChildCount; i++)
  {
    Mobile.SetCurrent(Mobile.Child(i).DeviceName, Mobile.Child(i).Index);
    TestedApps.android_calculator2.Run();
    ParentLayout = Mobile.Device().Process("com.android2.calculator3").RootLayout("").Layout("NO_ID");
    ParentLayout.Layout("simple_cling").Button("cling_dismiss").TouchButton();
    
    // Search for buttons by their ViewIDs
    PropNames = new Array("ObjectType", "ViewID");
    PropValues = new Array("Button", "digit1");
    ParentLayout.FindChild(PropNames, PropValues, 1000, true).Touch();
    PropValues = new Array("Button", "plus");
    ParentLayout.FindChild(PropNames, PropValues, 1000, true).Touch();
    PropValues = new Array("Button", "digit2");
    ParentLayout.FindChild(PropNames, PropValues, 1000, true).Touch();
    PropValues = new Array("Button", "equal");
    ParentLayout.FindChild(PropNames, PropValues, 1000, true).Touch();
  }
}

Python

def Test1():
  for i in range (0, Mobile.ChildCount-1):
    Mobile.SetCurrent(Mobile.Child(i).DeviceName, Mobile.Child(i).Index);
    TestedApps.android_calculator2.Run();
    ParentLayout = Mobile.Device().Process("com.android2.calculator3").RootLayout("").Layout("NO_ID"); 
    ParentLayout.Layout("simple_cling").Button("cling_dismiss").TouchButton();
    
    # Search for buttons by their ViewIDs
    PropNames = Array["ObjectType", "ViewID"];
    PropValues = Array["Button", "digit1"];
    ParentLayout.FindChild(PropNames, PropValues, 1000, True).Touch();
    PropValues = Array["Button", "plus"];
    ParentLayout.FindChild(PropNames, PropValues, 1000, True).Touch();
    PropValues = Array["Button", "digit2"];
    ParentLayout.FindChild(PropNames, PropValues, 1000, True).Touch();
    PropValues = Array["Button", "equal"];
    ParentLayout.FindChild(PropNames, PropValues, 1000, True).Touch();

VBScript

Sub Test1()
  Dim i, ParentLayout, PropNames, PropValues
  i = 0
  While i < Mobile.ChildCount
    Call Mobile.SetCurrent(Mobile.Child(i).DeviceName, Mobile.Child(i).Index)
    Call TestedApps.android_calculator2.Run
    Set ParentLayout = Mobile.Device.Process("com.android2.calculator3").RootLayout("").Layout("NO_ID")
    Call ParentLayout.Layout("simple_cling").Button("cling_dismiss").TouchButton
    
    'Search for buttons by their ViewIDs
    PropNames = Array("ObjectType", "ViewID")
    PropValues = Array("Button", "digit1")
    Call ParentLayout.FindChild(PropNames, PropValues, 1000, True).Touch
    PropValues = Array("Button", "plus")
    Call ParentLayout.FindChild(PropNames, PropValues, 1000, True).Touch
    PropValues = Array("Button", "digit2")
    Call ParentLayout.FindChild(PropNames, PropValues, 1000, True).Touch
    PropValues = Array("Button", "equal")
    Call ParentLayout.FindChild(PropNames, PropValues, 1000, True).Touch
    i = i + 1
  WEnd
End Sub

DelphiScript

procedure Test1;
  var i, ParentLayout, PropNames, PropValues : OleVariant;
begin
  i := 0;
  while i < Mobile.ChildCount do
  begin
    Mobile.SetCurrent(Mobile.Child(i).DeviceName, Mobile.Child(i).Index);
    TestedApps.android_calculator2.Run;
    ParentLayout := Mobile.Device.Process('com.android2.calculator3').RootLayout('').Layout('NO_ID');
    ParentLayout.Layout('simple_cling').Button('cling_dismiss').TouchButton;
    
    //Search for buttons by their ViewIDs
    PropNames := ['ObjectType', 'ViewID'];
    PropValues := ['Button', 'digit1'];
    ParentLayout.FindChild(PropNames, PropValues, 1000, true).Touch;
    PropValues := ['Button', 'plus'];
    ParentLayout.FindChild(PropNames, PropValues, 1000, true).Touch;
    PropValues := ['Button', 'digit2'];
    ParentLayout.FindChild(PropNames, PropValues, 1000, true).Touch;
    PropValues := ['Button', 'equal'];
    ParentLayout.FindChild(PropNames, PropValues, 1000, true).Touch;
    i := i + 1;
  end;
end;

C++Script, C#Script

function Test1()
 {
   var i, ParentLayout, PropNames, PropValues;
   for(i = 0; i < Mobile["ChildCount"]; i++)
   {
     Mobile["SetCurrent"](Mobile["Child"](i)["DeviceName"], Mobile["Child"](i)["Index"]);
     TestedApps["android_calculator2"]["Run"]();
     ParentLayout = Mobile["Device"]["Process"]("com.android2.calculator3")["RootLayout"]("")["Layout"]("NO_ID");
    ParentLayout["Layout"]("simple_cling")["Button"]("cling_dismiss")["TouchButton"]();
     
     // Search for buttons by their ViewIDs
     PropNames = new Array("ObjectType", "ViewID");
     PropValues = new Array("Button", "digit1");
     ParentLayout["FindChild"](PropNames, PropValues, 1000, true)["Touch"]();
     PropValues = new Array("Button", "plus");
     ParentLayout["FindChild"](PropNames, PropValues, 1000, true)["Touch"]();
     PropValues = new Array("Button", "digit2");
     ParentLayout["FindChild"](PropNames, PropValues, 1000, true)["Touch"]();
     PropValues = new Array("Button", "equal");
     ParentLayout["FindChild"](PropNames, PropValues, 1000, true)["Touch"]();
   }
 }

See Also

Running Tests on Multiple Devices
Getting Device's Screen Resolution
Checking Device Screen Orientation

Highlight search results