Best Practices for Organizing Locators for XCUITest

Best Practices for Organizing Locators for XCUITest

In the last post, we organized XCUITest tests in the BDD format by writing Swift extensions in the form of Step Definitions so they are more readable and scalable for iOS app testing. XCUITest identifies the element on the screen by using XCUIElement class. At the moment, we have our locators placed in the step definitions, which means when UI changes in the main app, we have to update all instances of locators from the step definitions. This isn’t a great approach to organizing XCUIElement in the test framework. In this post, we will explore the options of organizing UI elements for XCUITests and why Swift enumeration is the best strategy.

Page Object Pattern

The most common pattern used for organizing locators is PageObject pattern. This pattern has been highly recommended by the Selenium project and the details have been added to the Wiki in order to encourage test developers to use it in their test framework. In a summary, the page object is the class or struct defining all the common elements and basic functions that page or screen can offer. The page objects then can be referred in the step definitions or test methods so that when UI changes we just need to update the page object without affecting the test implementation. In our XCUITest101 app, we can write a class with locators of the enter button and Welcome text as:

class WelcomePage {
    static let enter = XCUIApplication().buttons["enter"]
    static let welcomeText = XCUIApplication().buttons["Welcome to XCUITest"]
}

After defining the page element in the class, we can then access UI elements from our step definition and tap on it usingWelcomePage.enter.tap() without any issue. This pattern works well and might be convenient for the test developer coming with a Selenium background. However, using Page Object Pattern has some limitation when used with Swift and XCUITest. 

  • Page Object Pattern is an object-oriented way of designing tests but Swift is a protocol-oriented programming language. Some of the OOPS concepts don’t always make sense in the Swift programming. 
  • By using Page Objects in XCUITest, we have an additional burden of maintaining inheritance and managing instances of the class.  
  • Page Object Pattern works well for web apps but for mobile apps, it needs customization. 

There are some other patterns like Screen Play which are mobile-specific but they all rely on the object-oriented principles. In the later section, we will cover the approach which is suitable for Swift’s protocol oriented nature with enumerations. 

Swift Enumerations for Organizing XCUIElements

Swift has enumeration a.k.a enums which are the first-class type in their own rights, which allows us to define the common type for the group and work with them. Swift enumeration has all the features that are supported by classes such as computed properties, instance methods, protocol conformance etc. You can read more Swift enumeration on Apple’s official docs here

We can make use of Swift Enumerations in order to store our UI elements also called XCUIElements. Swift enums play much better than classes and structs as we can add functions to the enumerations as needed. Another benefit of using enums is that we can access the enums all over the UI target without the need of creating objects or referring as a static value. With enums, we can 

  • Group XCUIElements by type e.g buttons, static texts
  • Assigning values to enumeration cases, usually accessibility identifiers of the elements and access the raw values of the cases in XCUIElements.
  • Use computed property features of Swift to dynamically return XCUIElements. 
  • The Swift enums can be accessed from anywhere within XCUITest target without the need of creating an instance or maintaining inheritance.

Now let’s use Swift enumerations in our demo app XCUITest101 to organize the XCUIElement in a better way. Let’s create a new Swift file in the XCUITest101UITests target from Xcode and call it WelcomeScreen as shown below. 

We can define all the elements and common functions in the WelcomeScreen.swift by using Swift enumerations. As we have only two XCUIElements i.e enter as UIButton and “Welcome to XCUITest” as Static Text, we can add them as enum cases with accessibility inspectors or labels as associated values. We can then use the computed property for returning the dynamic locators as XCUIElements. The enum code is available on Github here

In the code above, buttons and static texts to be returned as XCUIElements which allows us to easily group the common element types. Once we have done enumeration, we can access elements from anywhere using the WelcomeScreen enumeration like this: 

WelcomeScreen.enterButton.element

In our step definitions, we can replace the locators referring from the enumeration. Our step definition for taping on the enter button will look like this. 

func whenITapOnEnter() {
  XCTContext.runActivity(named: "When I tap on the enter button") { _ in
     WelcomeScreen.enterButton.element.tap()
        }
    }

As we can see that, the step definition becomes much more readable and changes in the UI won’t affect step definition directly. After this change, out tests will still pass. You can refer an article on a different approach to using Swift enumeration for XCUIElements here with more use cases. 

Try It Yourself 

The source code used in this tutorial is available on Github XCUITest101 repository and the enum-locatorsbranch here, download the repository and explore the XCUITest with Swift enums. From the command line, you can get the source code: 

 $ git clone https://github.com/Shashikant86/XCUITest101
 $ cd XCUITest101
 $ git checkout enum-locators
 $ open XCUITest101.xcodeproj/

Once the project gets opened in the Xcode 10, then press CMD+U to run the XCUITest. If you want to try it out on some free iOS devices in a device cloud, you can check out what Bitbar Testing has to offer.

Conclusion 

With Swift enumeration, we can make our XCUITests much scalable and organized while respecting the protocol-oriented nature of the Swift. We can achieve much more dynamic locators strategy using Swift enums than the traditional page object pattern. How do you organize your XCUIElements for Xcode UI tests? Share your experiences in the comments below. 


An Essential Guide to XCTest Framework for iOS App Testing

Get all essentials about XCTest framework and learn how to get started with it for cloud testing

Download