Learning UITesting using Xcode

Let me demystify this essential tool to testing your Apps

IO tests allow direct interaction with the App, with the simulator providing simulated touches and swipes with the App. This is opposed to the use of Unit testing. While Unit testing is great — you split up different sections of the App and then test them — it does not provide a wholistic view of how the App is behaving.

Luckily, Xcode is shipped with a UITesting framework built right in.

Prerequisites:

  • Some understanding of testing using Xcode

Terminology

UITesting: The process of testing the User Interface (UI) for Xcode apps

XCUIElement: A UI element in an application, that provides interactions with the UI like tapping, pressing, swiping, pinching and rotation

XCUIApplication: A proxy for an application that can be launched and terminated

XCUITest: An automation framework that ships with Xcode

XCTest: A framework that allows iOS developers to create and run unit, performance and UITests for Xcode projects

The project

I’ve got a project that I’ve used in a post about animations on UIPageView (https://medium.com/@stevenpcurtis.sc/an-animated-uipagecontrol-from-scra-75bc919d3174).

There are several was that the page can be scrolled — either through gestures or pressing the inviting looking next button on the screen.

So how can this project be tested?

The title of this article should be a clue — Unit tests are not really the way to go with this project.

The standard UITests setup

UITests can be rather demanding, so you probably want continueAfterFailure to be set to false, which means that execution will stop if there is a failure.

The App is set up as an XCUIApplication() and then can be launched.

UI Recording

At the bottom of the screen there is a very primising looking recording button. This becomes available when you create a test function with any applicable name in the UITests, for example

func testSwipes() {}

and place the cursor just between the curly braces to enable the record function for that particular test

Image for post
Image for post

This can be pressed, and it records the actions you do.

I swiped a couple of times, and the following code was generated:

We can then make assertions through XCTAssertTrue statements

Something like

XCTAssert(app.staticTexts[“Use your device microphone to say the color displayed”].exists)

(lots of these rather basic functions are avaliable here: https://kapeli.com/cheat_sheets/Xcode_UI_Testing.docset/Contents/Resources/Documents/index )

Issues?

At times I came across the following error:

Image for post
Image for post

Occasionally restarting the simulator helped with this, but on other occasions this is not true and we need to resort to other skills as detailed below:

Finding XCUIElements using staticTexts

We can manually find the pageIndicator, and then check that a button exists on the screen (after the indicator has been tapped) that has “Learn” written on it.

However this approach is extremely fragile. What if the target text changes at some point (answer: Our UITest would also fail)

Finding XCUIElements using Accessibility Identifiers

These can be set through the storyboard

Image for post
Image for post

However in my case I am reusing the same component through multiple instances of the view (look at the code if this does not make sense to you).

I set my particular accessibilityIdentifier through viewDidLoad

Now the issue may be that with larger Apps that you do know know which accessibilityIdentifier you set. No problem — just use the Accessibility Inspector.

Using the Accessibility Inspector

This is rather a great tool.

Right-click on the Xcode icon and select Open Developer Tool> Accessibility Inspector.

Image for post
Image for post

Inside the Accessibility inspector choose your device or simulator

Image for post
Image for post

You can then use the inspection pointer which looks rather like a target in Accessibility Inspector

Image for post
Image for post
The inspection pointer is in the middle of this screenshot

Now, running the App as usual lets you identify individual elements on the screen

Image for post
Image for post

Which in turn gives you the ability to see the identifier! In this case, the identifier is “Learn”

Image for post
Image for post

Waiting for transitions

You may have noticed above

.waitForExistence()

gave us the ability to wait for a transition to finish.

The details

Tapping a button

app.buttons["add"].tap()

Typing text

textField.typeText("hello")

Providing (of course) that you have identified and selected the textfield first with

app.textFields["input"]
app.tap*(

Dismissing alerts

app.alerts["Alert Title"].buttons["title"].tap()

Using sliders

app.sliders.element.adjustToNormalizedSliderPosition(0.5)

Using Pickers

app.adjustToPickerWheelValue("first value")

where the picker has first been identified with

let firstPickerPredicate = NSPredicate(format: "label BEGINSWITH 'First Picker'")
let firstPicker = app.pickerWheels.elementMatchingPredicate(firstPredicate)

Pull to refresh

Select the cell

let cell = app.staticTexts["AA"]

then choose the vector in which to drag

let start = cell.coordinateWithNormalizedOffset(CGVectorMake(0, 0))
let end = cell.coordinateWithNormalizedOffset(CGVectorMake(0, 6))
start.pressForDuration(0, thenDragToCoordinate: finish)

Detect whether a view controller is on top of the navigation stack

XCTAssert(app.navigationBars["Title"].exists)

The full repo link:

Want to get in contact? Try the link here:

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store