Using XCUITest framework for automation of iOS apps makes sure that app is working as expected from the users points of view. Since, Apple launched the UI testing support in WWDC 2015, this framework getting popular among iOS developers as they can now write UI tests in Swift and XCTest frameworks same as unit tests. In this short post we will see how to organise the XCUIElements i.e locators on the screens using Swift enums in better way. This is the short trick shared by one of my smart colleague at work.
Organising Screen UI Elements
In the Selenium or Appium world, you might have heard some patterns like Page Objects or Screen Play that has been used in order to abstract or store UI elements. We can store the UI elements in the separate class or Struct and then we can access those elements in the tests or step definitions etc. In the Swift, we can make use of Swift Enumerations in order to store our UI elements also called XCUIElements. Swift enums plays 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 need to creating objects or refer as static value.
Using Swift Enums for XCUIElement
Let’s imagine that our iOS app has home screen which contains 3 buttons, 2 static texts. We can easily write an enumeration with all these 6 cases like this.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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 that, we have defined all the elements in the home screen using Swift enum that can be used anywhere in the UI test target. This is an approach, I have taken previously which also explained in the BDD tool XCFit which I wrote earlier. There is nothing wrong in this approach as we can access all those element independently as needed. However, there is better way we can use Swift enums so that we can save lot of code and still achieve the same result.
Better Approach of using Swift enums
In the approach mentioned earlier, we have defined all the elements separately, we can refactor above enum by
- Grouping the XCUIElement by type e.g buttons, static texts
- Assigning the values to enumeration cases, usually accessibility identifiers of the elements
We can then use raw values of the cases in the XCUIElements. So we can refactor above enum as shown below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
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 that, we have assigned the string values to enum cases and ew grouped the cases as buttons and static texts. This makes our original enum much shorter and concise.
Even Better Approach
If you want to keep the types separate, we can re-write the enum something like this :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
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 that, we can easily group the elements types e.g buttons, staticTexts, images etc.
You can pick an approach whatever seems suitable.
Conclusion
By using some great features of Swift programming language, we can make our XCUITests much smarter, faster and 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 better approach of storing XCUIElements in the comments.