iOS UI Automation with XCUI: Part 1 - a Layered Clean Design

Anil Telaich
VMware 360
8 min readMay 11, 2020

--

Automation of UI testing on iOS has become as easy as writing unit tests ever since Apple introduced the XCUI framework. In this post, I am going to talk about how XCUI, coupled with layered design, can help easily automate applications, especially those with multiple customised UI elements. One such application where we used this extensively is VMware Boxer, an enterprise application from VMware thats aggregates three important functions - Email, Calendar and Contacts. VMware Boxer uses multiple types of UI elements from native UITableView to highly customised visual elements.

It is assumed that readers have a basic understanding of Xcode, Objective C, Swift, XCUI framework and the Apple development ecosystem in general. If you are new to XCUI, you should first start here.

Making a choice : Which framework to use for automation?

Like any other UI automation project, our team started with a question about which automation framework to use. We had been using SeeTest. However, we saw this as an opportunity to review our automation strategy given the new, complex functionality added in VMware Boxer, the number of products in VMware ecosystem and integration testing across these products. Below were the choices we considered-

1. Continue with SeeTest

2. Earl Grey

3. XCUI

While SeeTest had the capability to handle our immediate needs to automate, there was an independent challenge to deal with. It required us to adopt a new technology stack. This would require additional training for iOS developers and dedicated experts to maintain the automation system.

What about Appium? We did not consider Appium for the same reasons mentioned above for SeeTest. Appium is a good choice if we had hybrid apps or significant shared code between Android and iOS.

Earl Grey 1 was a possible choice, given that it allowed us to stay in Xcode and Apple development tools and it was based on XCTest. Earl Grey 1 automation, however, runs in the same process as the application under test. On the other hand, Apple’s XCUI framework-based automation runs in a separate process than the application under test. This is preferred since the application under test should be nothing more than a black box for the entity (an automation process or an end user) that is testing its UI.

Have the automating (one which is testing) process isolated from the actual application under test. The application under test should be nothing more than a black box for the automating process.

Any new tool in the Apple ecosystem that is not aligned with Apple’s approach (e.g. Earl Grey 1) may fail without warning. Such an approach may require a lot of effort to maintain to continue to work with newer or future offerings from Apple.

Early Grey 2 added the capability to interact with XCUI. It was an attempt to align with XCUI but it had these known issues.

Eventually, XCUI was the preferred choice. With this, a developer will have more control over automation and will be the immediate beneficiary of any innovation in Apple’s ecosystem.

Getting Started

App under test and XCUI automation module

Before we jump in to the details, it’s important to reiterate that the automation module using XCUI runs as an independent process from the application which is under test. To start off, we added a new module that we will refer to now onward as the Automation Module(AM). AM is a workspace containing a collection of classes and data structures used for VMware Boxer automation using XCUI.

Accessibility Identifiers

It’s important that the application be developed keeping its testability in mind. One of the responsibilities of the developer is to ensure that each user interface element that is exposed to the end user has the accessibility identifier set during development. The accessibility identifier is the link between the application under test and the XCUI automation process.

The accessibility identifier is the link between the application under test and the XCUI automation process.

Design Pattern

As mentioned earlier, VMware Boxer is a huge application. The choice of design pattern to be used was arrived at keeping in mind that the tests should be easy to implement, easy to maintain, reusable, robust and scalable. The content below explains the layers of the XCUI Automation module’s architecture for VMware Boxer:

The following figure shows the basic building blocks:

The order of invoking of layers

Tests Classes

This is the layer that has the classes having the test methods. The intention of this layer is to have tests comprising of minimal code that says only what is being tested. Also, the developer implementing the tests needs to only think about the high-level application flow required to test what is being tested. The rest of the complexity (e.g. how to navigate to the UI where the desired testing can be performed) should be abstracted to reusable layers. Refer the image below that shows an example test method explaining the above intent — the code tests if a Settings UI can help enabling/disabling a feature or if it has required switch controls.

Example Test

So, the developer who is writing the tests could write code that is as simple as pseudo code. In the meantime, it is clear that some logic should be invoked from next layer (Flows : explained in next section).

Flows Classes

Flows represent a collection of classes that provides functionalities required for various Tests classes. Functionalities might include setting up the app before executing the real test case or navigating to a certain part of the app under test. Most of these functionalities are reusable and required in multiple tests. e.g. the above sample test case requires that 1. the app be launched. and 2. We navigate to a Feature Controller settings screen.

So, Flow classes would be invoked from Tests and would implement flows like “Navigate to Settings” or “Navigate to Feature Controller Settings”.

Screens Classes

Screens are classes which have a one-to-one mapping to respective View Controllers. A View Controller can be, at the high level, seen as a class having (in the backing view) UIControls and methods that respond to user interaction with those UIControls. A Screen class is simply a proxy mapping of the View Controller. The figure below shows this mapping. There is a cell Initial View in the UI and when user taps on this cell, the user can select what should be marked as the initial view for the app. The figure below shows how a Screen class is implemented to map the above requirement.

The pseudo code for this screen class would look like -

// The class models the SettingFormViewController for XCUI tests.public class SettingsFormScreen { 
private static let navBarId = "Boxer.Settings.NavigationBar"
private lazy var featureControllerCell = boxerApp.application.tables.cells[AccessibilityIdentifiers.featureControllerCellId]
public func navigateToFeatureController() -> Bool {
return // tap on Cell with featureControllerCellId
}
}
class AccessibilityIdentifiers: NSObject {// MARK: Settings Screenstatic let featureControllerCellId = "Boxer.Settings.FeatureControllerCell"
}

what is boxerApp in above code? In above code boxerApp represents a unit which is an instance of BoxerApp class that encapsulates the application under test and its screens. This is explained in the next section.

The code above models the SettingFormViewController of VMware Boxer. The convention used in naming the class is that name is derived from the name of view controller by replacing the suffix ViewController with Screen. Also note the convenience class that has accessibility identifiers.

The accessibility identifier is the common link between application under test and the XCUI automation process.

Putting it Together: VMware Boxer XCUI Automation Module

The UITest module tests the running application. It is important the application model be designed such that one instance of the app under test is a single unit. This means that the application under test should be seen as a collection of screens classes participating in business flows (collection of flow classes)which are invoked from the tests (collection of classes)based on the requirement of the test. Each application unit should have exactly one instance of each Screen class that corresponds to a one view controller.

How to achieve this?

An XCUITest suite can achieve this by encapsulating the application under test instance(XCUIApplication) in one unit e.g. BoxerApp class which has a member application and has all the screens for that application. The figure below explains this encapsulation

Therefore, before the XCUITest suite starts, the first thing it would do is to create an instance of the BoxerApp module using the bundle identifier of the application under test. The figure below explains this

Initialising the app module

This instance of BoxerApp (the unit encapsulating the XCUIApplication and Screens) is then passed by the Tests::testMethod to Flows::flowMethod as first parameter.

Tests Calling Flow Methods

The Flows::flowMethod in turn calls to Screen::screenMethod and passes BoxerApp if required.

Flow Method Calling Screen Methods

Finally the interaction with application UI happens through Screen::screenMethod.

Tapping A Button through Screen Method

The figure below shows the above design.

Conclusion

Writing XCUI tests can be cumbersome and unmanageable if it is not done with a plan. I hope this post provides helpful pointers on how to think about structuring your automation project in a modular, clean, layered way.

What’s next?

I am going to cover these topics in the series of stories I am planning:

XCUI : Part 2 -Interacting with Native applications in a clean way

XCUI : Part 3 -How XCUI runtime can interact with the application under test in a clean way.

XCUI : Part 4 -Handling System Alerts.

XCUI : Part 5 -Observing Test Progress and accessing reports and capturing Screenshots.

References

Gupta, Amit. “Efficient UI Test Automation: Decoupled Element Identifiers
Services” European Journal of Advances in Engineering and Technology, vol. 5, no. 2, May. 2018, pp. 136–141, ejaet.com/PDF/5–2/EJAET-5–2–136–141.pdf.

Gupta, Amit. “Balancing Complexity and Efficiency Strategies for Multi Application Test Automation Using Shared Code Frameworks.” European Journal of Advances in Engineering and Technology, vol. 5, no. 12, Dec. 2018, pp. 1034–1042, ejaet.com/PDF/5–12/EJAET-5–12–1034–1042.pdf.

--

--

Anil Telaich
VMware 360

a keen iOS developer with special interest in Architecture, Design and people enablement in what makes a real difference in what they care about..