Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Organizing XCUIElements With Swift Enumerations

DZone's Guide to

Organizing XCUIElements With Swift Enumerations

In this quick walkthrough, iOS developers can learn a better way of organizing XCUIElements, or locators, using Swift enumerations.

· Mobile Zone ·
Free Resource

Using the XCUITest framework for the automation of iOS apps makes sure that the app is working as expected from the user's points of view. Since Apple launched UI testing support at WWDC 2015, this framework has been popular among iOS developers, as they can now write UI tests in Swift and XCTest frameworks the same as unit tests. In this short post, we will see how to organize the XCUIElements,  i.e. locators, on the screens using Swift enums in a better way. This is a short trick shared by one of my smart colleagues at work.

Organizing Screen UI Elements

In the Selenium or Appium world, you might have heard of some patterns like Page Objects or Screen Play that have been used in order to abstract or store UI elements. We can store the UI elements in a separate class or Struct and then we can access those elements in the tests or step definitions, etc. In Swift, 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 we can access the enums all over the UI target without the need to create objects or refer as a static value.

Using Swift Enums for XCUIElement

Let's imagine that our iOS app has a home screen which contains three buttons and two static texts. We can easily write an enumeration with all six cases like this:

import XCTest
enum HomeScreen: String {
    case guestButton
    case registerButton
    case loginButton
    case welcomeText 
    case introText 
    var element: XCUIElement {
        switch self {
        case .guestButton:
            return XCUIApplication().buttons["Hello"] 
        case .registerButton:
            return XCUIApplication().buttons["Register"]
        case .loginButton:
            return XCUIApplication().buttons["Login"]
        case .welcomeText:
            return XCUIApplication().staticTexts["Welcome"]
        case .introText:
            return XCUIApplication().staticTexts["Introduction to app"]
        }
    }
}

Now we have defined all the elements on the home screen using a Swift enum that can be used anywhere in the UI test target. This is an approach I have taken previously, which is also explained in the BDD tool XCFit, which I wrote earlier. There is nothing wrong with this approach, as we can access all those elements independently as needed. However, there is a better way we can use Swift enums so that we can save a lot of code and still achieve the same result.

A Better Approach to Using Swift Enums

In the approach mentioned earlier, we defined all the elements separately. We can refactor above enum by

  • Grouping the XCUIElements by type, e.g. buttons, static texts.
  • Assigning the values to enumeration cases, usually accessibility identifiers of the elements.

We can then use the raw values of the cases in the XCUIElements. So, we can refactor the above enum as shown below:

import XCTest
enum HomeScreen: String {
    case guestButton = "Hello"
    case registerButton = "Register"
    case loginButton = "Login"
    case welcomeText = "Welcome"
    case introText = "Introduction to app"
    var element: XCUIElement {
        switch self {
        case .guestButton, .registerButton, .loginButton :
            return XCUIApplication().buttons[self.rawValue] 
        case .welcomeText, .introText:
            return XCUIApplication().staticTexts[self.rawValue]
        }
    }
}

Now we have assigned the string values to enum cases and grouped the cases as buttons and static texts. This makes our original enum much shorter and more concise.

An Even Better Approach

If you want to keep the types separate, we can rewrite the enum something like this:

enum HomeScreen: String {
    case guestButton = "Hello"
    case registerButton = "Register"
    case loginButton = "Login"
    case welcomeText = "Welcome"
    case introText = "Introduction to app"
    var element: XCUIElement {
        if buttonElements.contains(self) {
            return XCUIApplication().buttons[self.rawValue]
        }
        if textElements.contains(self) {
            return XCUIApplication().staticTexts[self.rawValue]
        }
        fatalError("Element not found")
    }
 
    private var buttonElements: [HomeScreen] {
        return [.guestButton, .registerButton, .loginButton]
    }
 
    private var textElements: [HomeScreen] {
        return [.welcomeText, .introText]
    }
}

Now we can easily group the elements types, e.g. buttons, staticTexts, images, etc.

You can pick whichever approach seems suitable.

Conclusion

By using some of the great features of the Swift programming language, we can make our XCUITests much smarter, faster, and more scalable. In this example, we have seen how to use Swift enumerations for storing XCUIElements. Later, I will share more XCUITest tips as I learn from experts. Let me know if you have a better approach to storing XCUIElements in the comments.

Topics:
mobile ,mobile app development ,ios ,swift ,ui testing

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}