XCUITests — Best Practices for Organizing Locators with Swift Enumerations

Nishith Shah
Quality Engineering University
4 min readJun 13, 2020

In this post, Automation Engineer can learn a better way of organizing locators for XCUITests using Swift enumerations.

Using the automation of any application makes sure that the application is working as expected. Since Apple introduced UI testing support at WWDC 2015, this framework has been popular among iOS developers and later on adopted by Automation Engineers, as they can now write UI tests in Swift. In this post, we will see how to organize the locators, on the screens using Swift enumerations in a better way.

Photo by David Pupaza on Unsplash

Best Way To Store Locators

While using Selenium, Appium or any other automation framework, the most common pattern used for organizing locators is Page Object pattern. This pattern has been highly recommended by most of the Automation experts to use it in their test framework. We can store the locators in a separate class or Struct and then we can access those locators in the tests or step definitions or pages, etc.

In XCUITests using Swift, we can use Swift Enumerations to store our locators. Enums play much better than classes or structs. We can also leverage the feature or Swift Enumerations and these enums are accessible all over the UI Target. We don’t have to create an object and it refers to a static value.

Let’s assume that our iOS app has a Login screen which has Username, Password as Text fields, and Login button.

Generally, when people start writing the Test Case, they would like to write a script in this manner

let app = XCUIApplication()app.textFields["userName"].tap()
app.textFields["userName"].typeText("Enter Username")
app.secureTextFields["password"].tap()
app.secureTextFields["password"].typeText("Enter Password")
app.buttons["Login"].tap()

Here, we have to rewrite the code to locate elements. This would increase the maintenance efforts in terms of the Time and Money.

Here is the way to make it reusable using Swift Enumerations.

Swift Enums to Store Locators

Create the Login Screen Enum as following

enum LoginScreen: String {    case userNameTextField
case passwordTextField
case loginButton
var element: XCUIElement {
switch self {
case .userNameTextField:
return XCUIApplication().staticTexts["userName"]
case .passwordTextField:
return XCUIApplication().secureTextFields["password"]
case .loginButton:
return XCUIApplication().buttons["Login"]
}
}
}

How to use it in our script

LoginScreen.userNameTextField.element.tap()
LoginScreen.userNameTextField.element.typeText("Enter Username")
LoginScreen.passwordTextField.element.tap()
LoginScreen.passwordTextField.element.typeText("Enter Password")
LoginScreen.loginButton.element.tap()

There is nothing wrong with this approach. But if we look into the LoginScreen enum, we mentioned XCUIApplication().staticTexts[“..”] twice. Now assume about the application form which asks to fill 20 such text fields. We have to write 20 enum cases, which is not a good way. Here is the better way to reduce the Line of Code and still achieve the same results.

A Better Way to Use Swift Enum

enum LoginScreen: String {
case userNameTextField = "userName"
case passwordTextField = "password"
case loginButton = "Login"
case registerButton = "Register"
case forgotPassButton = "Forgot Password?"
var element: XCUIElement {
switch self {
case .userNameTextField:
return XCUIApplication().textFields[self.rawValue]
case .passwordTextField :
return XCUIApplication().secureTextFields[self.rawValue]
case .loginButton, .registerButton, .forgotPassButton:
return XCUIApplication().buttons[self.rawValue]
}
}
}

Here, we have assigned the accessibility ids or string value to the case and grouped the element type. This will reduce the line of code to add a new locator.

Is this a better way? Do you think that we have to update Switch Case condition as needed? Can me make it even more better?

An Even Better Way to Use Swift Enum

enum LoginScreen: String {
case userNameTextField = "userName"
case passwordTextField = "password"
case loginButton = "Login"
case registerButton = "Register"
case forgotPassButton = "Forgot Password?"
var element: XCUIElement {
if buttonElements.contains(self) {
return XCUIApplication().buttons[self.rawValue]
}
if textFieldsElements.contains(self) {
return XCUIApplication().staticTexts[self.rawValue]
}
if secureTextFieldsElements.contains(self) {
return XCUIApplication().secureTextFields[self.rawValue]
}
XCTFail("Element not found")
}
private var buttonElements: [LoginScreen] {
return [.loginButton, .registerButton, .forgotPassButton]
}
private var textFieldsElements: [LoginScreen] {
return [.userNameTextField]
}
private var secureTextFieldsElements: [LoginScreen] {
return [.passwordTextField]
}
}

Now, we can easily group locators by its element type. Like buttons, textfields, images, statisTexts, etc.

Even Much Better Way????

Can we eliminate the dependency of Element Type to locate element?

How would you handle if developer will change the Element Type but not the Accessibility Id?

Can we achieve the same result by just writing the following enum?

enum LoginScreen: String {
case userNameTextField = "userName"
case passwordTextField = "password"
case loginButton = "Login"
case registerButton = "Register"
case forgotPassButton = "Forgot Password?"
}

Think Your Self!!!

Conclusion

By using some of the great features of the Swift programming language, we can make our XCUITests much smarter, faster, and more scalable.

With Swift enumeration, we can achieve a much more dynamic locators strategy using Swift Enumerations.

So, before jump directly in XCUITests, Be an Expert in Swift Programming Language and make your script much Smarter.

Let me know if you have a better way to storing Locators.

Let me know — along with any questions, comments, or feedback that you might have on https://www.linkedin.com/in/nshthshah.

--

--