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

Architecting iOS XCUITests for iPhones and iPads

DZone's Guide to

Architecting iOS XCUITests for iPhones and iPads

Using UIDevice from Apple's XCUITest framework, you can test apps for both iPhones and iPads without duplicating code.

· Mobile Zone ·
Free Resource

Apple's XCUITest framework is a hot and emerging framework for UI test automation of iOS apps. Since it launched in WWDC 2015, it has gotten a lot of attention and enhancements. It allows us to write UI tests for iOS apps in Swift, which makes it easy for iOS developers to add UI tests for the iOS applications for both iPhone and iPads. It's essential to consider both iPhone and iPads for XCUITest, as users using apps on iPads can't be ignored. With XCUITest, continuous integration and continuous delivery became painless as unit and UI tests are written using the same framework, XCTest. That was not the case before XCUITest; tools like Appium and Calabash allowed us to write UI tests using other languages like Ruby or Java, which became a pain for CI/CD and the flow of iOS development. XCUITest can be written for iOS apps, however, iOS devices have different variants, like iPads, with different screen sizes and iPhones with different screen sizes. We have to make sure XCUITests run flawlessly on all variants without causing a lot of duplication. If the XCUITest is architected properly, then we can avoid duplication of code and still able to run all our XCUITest on different variations. In this post, we will cover how to architect XCUITest for both iPhone and iPad.

XCUITest

A newbie of XCUITest should definitely watch this WWDC video in order to understand the basic functionality of the XCUITest framework. XCUITest allows us to write tests for iOS apps using Apple's own programming language, Swift. It's way different from other third-party frameworks like Appium, Calabash, etc. If you want to compare XCUITest with Appium, then read this post to get a better understanding of each framework. If you want to see a hands-on exploration of all the new XCUITest features, then please refer to my previous blog post on New XCUITest Features with Xcode 9, where I covered almost all the features with detailed examples. You can setup Protocol Oriented architecture for XCUITests as mentioned in this post on DZone, and the second part is available here. We can make XCUITest scalable by using some of the techniques mentioned in WWDC 2017 talk on engineering for testability, which focuses on testable code.

UIDevice and XCUIDevice

Apple has provided the UIDevice class to get a representation of the current device and the XCUIDevice class to simulate physical buttons and device orientations. This makes architecting tests on iPhone and iPads so easy if we have organized XCUIElements properly. By using the UIDevice API, we can get the current device running the test using the following code:

if UIDevice.current.userInterfaceIdiom == .pad {
 
// Do iPad Stuff 
 
}

With XCUIDevice, we can set the device orientation to landscape or portrait using the following code:

XCUIDevice.shared().orientation = .landscapeRight

These two APIs will be super useful to set our XCUITests on both iPhones and iPads.

Avoid Having Two Suites

In many projects, I saw that people create two separate test suites for iPhones and iPads. There are two separate Xcode schemes executing the tests in iPads and iPhone. There is also conditional execution all over the tests, which makes its brittle to manage test suites. We don't need to do this as if we can use approach mentioned in this post. We will try to find the solution for these problems later in the post.

Organizing XCUIElements

While architecting XCUITests on iPads and iPhones, it's very important to organize XCUIElements in the proper format. I strongly recommend using Swift enumerations for organizing elements on screens. Please refer to this post for the protocol-oriented architecture of XCUITest. In my recent post on organizing XCUIElements with Swift enumerations, I have explained how to organize elements as per the screen. Let's imagine that our iOS app has a home screen which contains three buttons and two static texts. We can write the enum like this:

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().navigationsBars["XYZ"].buttons[self.rawValue] 
        case .welcomeText, .introText:
            return XCUIApplication().staticTexts[self.rawValue]
        }
    }
}

We have assigned the string values to enum cases and grouped the cases as buttons and static texts.

Conditional XCUIElements on iPads

The enum mentioned above will work perfectly for the iPhone as all the buttons are part of the navigation bar. However, if we have a situation with an iPad design, all the buttons are in the Tab bar. The approach mentioned in the enum will not work for iPads. However, by using UIDevice, we can conditionally return XCUIElements from the above enum if the device is an iPad, 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 :
             if UIDevice.current.userInterfaceIdiom == .pad {
                 return XCUIApplication().tabBars["XYZ"].buttons[self.rawValue]
             } else {
                 return XCUIApplication().navigationsBars["XYZ"].buttons[self.rawValue]
             } 
        case .welcomeText, .introText:
            return XCUIApplication().staticTexts[self.rawValue]
        }
    }
}

Now that enum will return the buttons from tab bars for iPad and navigation bar for iPhone. We don't have to touch our actual test implementation from where these XCUIElements will be accessed. Using this approach, we can avoid a lot of duplication of the code, as well as make our test suite accessible for all device variant and orientations.

Conclusion

With the use of the protocol-oriented architecture of XCUITest, Swift enumerations, and the UIDevice API from Apple, we can achieve UI test automation on iPhone and iPad devices without repeating code. There is no need to create separate schemes or add conditions everywhere in the test code for iPad scenarios. I hope you like this approach for architecting tests for both iPhones and iPads. I'd be happy to learn what you think about this approach, and if you think of a better approach, let me know.

Topics:
mobile ,tutorial ,xcuitest ,ios ,mobile testing ,ipad ,test automation ,swift ,ci/cd ,unit 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 }}