How to Set Up Xcode UI Testing for Today Extensions

Blog

April 17, 2018

TABLE OF CONTENTS

Introduction

Today Extensions are a great way to add quick and glanceable UI to your app. Xcode UI Testing, along with iOSSnapshotTestCase (previously named FBSnapshotTestCase), is a great way to test UI in your app. So, why not use them together? Well, there are a few problems with that.

First, Xcode doesn’t allow a Today Extension target to be set as the target application for a UI Test. Second, iOSSnapshotTestCase doesn’t work out of the box with Xcode UI Testing.

I will cover workarounds for both of these issues below.

arrow-right

Our 2020 Legacy Modernization Report is here.

We surveyed hundreds of IT executives to understand their biggest challenges. See the survey results, symptoms of legacy systems, and our business solutions on modernization to improve business success.

Manipulating the World Outside of Your App

Since Xcode doesn’t allow a Today Extension target to be set as the target application for a UI Test, we need a way to get to the Today Extension after the app has launched. While using Xcode UI Test recorder is usually a great start to creating a UI Test, it won’t work without some tweaking. For one thing, it doesn’t record hitting the home button to get to the home screen. Getting to the homescreen and controlling it used to be fairly difficult and involved imported private headers, but now it’s easy—just call XCUIDevice.shared.press(XCUIDevice.Button.home). Then, once on the home screen, it records:


let scrollView = XCUIApplication().otherElements["Home screen icons"].otherElements["SBFolderScalingView"].children(matching: .scrollView).element

scrollView.swipeRight()

The problem here is that XCUIApplication is SpringBoard and not the current target application, so the test runner isn’t able to find “Home screen icons.” To fix this, we need to find SpringBoard’s scroll view like this:


XCUIDevice.shared.press(XCUIDevice.Button.home)

XCUIDevice.shared.press(XCUIDevice.Button.home) //Do this twice to go to the first home screen

let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")

let scrollView = springboard.otherElements["Home screen icons"].otherElements["SBFolderScalingView"].children(matching: .scrollView).element

scrollView.swipeRight() //Swipe over to the Today view

Now we have the Today View on screen. Note that this method is also useful for manipulating other apps, like Settings, in order to test permission changes, such as notifications settings for an app.

Getting iOSSnapshotTestCase to Play Nice with Xcode UI Testing

Typically, iOSSnapshotTestCase is used in a white box testing scenario in which a programmatically-created view is compared to a screenshot previously captured using iOSSnapshotTestCase in record mode. However, Xcode UI Testing is used in a black box testing scenario in which the running app is inspected.

To get set up, follow the instructions in iOSSnapshotTestCase’s ReadMe—except you will want to add iOSSnapshotTestCase to your UI Test target, instead of just your Unit Test target. To bridge the gap between the iOSSnapshotTestCase and Xcode UI Testing, simply create a UIImageView with an image captured during UI Testing:


sleep(1) //wait for swipe animation to finish

let image = springboard.screenshot().image

FBSnapshotVerifyView(UIImageView(image: croppedImage))

Next, set iOSSnapshotTestCase record mode to Yes, run the test, set record mode to No, run the test and … the test failed. Well, the test probably failed because taking a screenshot of the SpringBoard includes the status bar with the time, which more than likely changed since the reference screenshot was recorded.

To avoid this external state change, we need to crop the status bar out of the image. Fortunately, cropping the status bar is easy with Joseph Susnick’s UIImage extension. Add the extension to your UI Test target, and then call removingStatusBar on the screenshot image:


sleep(1) //wait for swipe animation to finish

let croppedImage = springboard.screenshot().image.removingStatusBar

FBSnapshotVerifyView(UIImageView(image: croppedImage))

It took a few hoops to jump through, but now you can verify the look and feel of your Today Extension.

Authored By

Steve Moser, Engineering Manager

Steve Moser

Engineering Manager

Meet our Experts

Steve Moser, Engineering Manager

Steve Moser

Engineering Manager

Steve Moser is an Engineering Manager at Levvel focusing mainly on iOS. He is passionate about leveraging software to deliver great experiences.

Let's chat.

You're doing big things, and big things come with big challenges. We're here to help.

Read the Blog

By clicking the button below you agree to our Terms of Service and Privacy Policy.

levvel mark white

Let's improve the world together.

© Levvel & Endava 2023