How to UI Test Push Notifications and Universal Links in the iOS Simulator

Renato Gamboa
Compass True North
Published in
7 min readJan 12, 2021

The Problem

At Compass, we have a goal to diminish the app regression time from 5 days, to under 5 hours. In order to achieve this, we aligned our focus on Automating Manual Test Cases.

The team decided that 5 days was way too long for a manual regression, especially if we wanted to follow a weekly release cadence.

Along this journey, we’ve found unique tools, built systems, and adopted processes to stay on track towards our ultimate goal. These efforts include running nightly UI Automation Tests on Bitrise with reporting to TestRail, a Robot Pattern test suite to assist our engineers in writing Automation Tests, and app-wide modularization.

Previously, the duration for testing Push Notifications manually (which involved a physical device and engineer) took anywhere from 2 hours to half an entire workday. This included triggering push notifications from a respective feature, then waiting roughly 20 minutes on our staging environment for each one to arrive and present itself on an iOS device. We verified the arrival of the push notification on the device as well as the proper routing within the app when it was tapped.

These were the most frustrating and goal-hindering set of integration tests for our team, and yes… QA’s were not happy.

Our Goal

Our iOS infrastructure team envisioned an objective: Automate 100% of Push Notifications and 100% of Universal Link UI Tests.

This idea seemed too far-fetched, but we were determined to manifest it.

In order to achieve this we had to answer these questions:

  • “How does one test push notifications instantly and efficiently on a simulator?”
  • “Are we able to directly test or trigger push notifications in XCUITests?”
  • “Would we be able to run it on our CI platform?”

Getting Closer

As of Xcode 11.4, users are finally able to test Push Notifications via the iOS Simulator.

We can send push notifications to the simulator in two different ways:

First (assuming our app has authorized for notifications), let’s take a look at the command line method from Xcode version 11.4 release notes:

simctl also supports sending simulated push notifications. If the file contains “Simulator Target Bundle” the bundle identifier is not required, otherwise you must provide it as an argument (8164566):

$ xcrun simctl push <device> com.example.my-app ExamplePush.apns

  1. We’ll replace <device> with our simulator’s device identifier, which we can retrieve like this:
Locating the simulator device identifier

2. Next, we’ll need to get our “Simulator Bundle Target”. Ours will be: com.compass.MusselExample

3. Then, we’ll create a sample notification payload .apns file. We’ll replace the “Simulator Target bundle” with our own

4. Lastly, we replace <device>, com.example.my-app , and ExamplePush.apns with the information we gathered from steps 1, 2, and 3 respectively. Our final command should look something like:

$ xcrun simctl push 942A6B1C-BE29-4E05-A075-5146088F7C9E com.compass.MusselExample PushNotificationTest.apns

Nice! Now let’s try our second technique, dragging and dropping the APNs file onto our simulator

Opening Universal or Deep Links in the simulator via command line is fairly similar as well, we can use:

xcrun simctl openurl booted <Universal Link>

Unfortunately, Apple has yet to introduce the ability to leverage this new method within the XCUITest Framework. This is necessary in order to run UI Tests nighly on our CI Platform, Bitrise.

How We Solved Our Problem

While in search of viable solutions, thanks to Matt Stanford and the Pterodactyl framework, we were able to pave the way for success in minimizing our regression time and build off this clever technique of launching a local macOS server to communicate with our iOS application while testing.

This was our starting point, this was our chance to build some Mussel.

Mussel introduces a quick and simple way to test Push Notifications and Universal Links routing to any specific features within your iOS application.

How It Works

Mussel Workflow
  1. An Engineer triggers XCUITests in Xcode manually or through your Continuous Integration platform of choice.
  2. Mussel Server (Mac Application) boots up along with the iOS Simulator.
  3. An XCUITest triggers a Push Notification or Universal Link Test Case.
  4. The Test Case sends a payload containing Push Notification or Universal Link data via POST Request.
  5. The Server runs the respective xcrun simctl command for Push Notifications or Universal Links (This is a feature included in Xcode Command Line Tools).
  6. The command presents a Push Notification or launches a Universal Link within the iOS Simulator.

How To Use Mussel

First, we’ll need to install the Mussel pod to the project’s UI Test Target in your Podfile

Then, we need to import the framework into our file

Push Notification UI Testing

Initialize your Mussel Tester of choice, we’ll start with the MusselNotificationTester. Use your Target App’s Bundle Id to ensure notifications are sent to the correct simulator.

To send a push notification with a simple message to your iOS Simulator

You can also send full APNS (Apple Push Notification Service) payloads for notifications with more complexity, supplying keys that are outside the aps field. You can specify this payload as a Dictionary

This dictionary is equivalent to the following APNS payload

Then call triggerSimulatorNotification with your respective dictionary-converted APNS payload

Mmm! How easy!

Universal Links UI Testing

Initialize your MusselUniversalLinkTester using your Target App's Bundle Id to ensure notifications are sent to the correct simulator

Trigger your iOS Simulator to open a Universal Link

Results

Testing Push Notification on Simulator with Mussel

A routine that previously took us up to half of an entire workday now completes in less than 8 minutes.

Testing Universal Links on Simulator with Mussel

As a bonus, we were able to test a subset of our 100+ unique Universal Links tests using Mussel, which completed in about 12 minutes.

How We Are Giving Back

In hopes of saving other organizations with UI and Automation Testing, we decided to Open Source this framework!

Why did we choose the name Mussel? We wanted to stay in the same family of the animal kingdom as our relative Edible Mollusc open-source project:

Snail — A lightweight observables framework.

For more information on how to use Mussel and configuring it to your own project, check out the Open Source Repository on Github:

Happy Testing with some Mussel 😎💪

If you’re looking for a job, Compass is hiring.

Mussel Facts

  • Along with all the other bivalves, mussels are a perfect brain food because they are packed with DHA and vitamin B-12, two key nutrients that are vital to protecting your brain health and preserving your memory as you age, along with some rare trace minerals on which a healthy, happy brain depends.
  • Mussels have been cultivated for almost 800 years in Europe, and have been used as a food source for more than 20,000 years. In fact, prehistoric settlements in Scotland can often be identified by the large mounds of mussel shells found nearby.
  • The word “mussel” derives from the Latin diminutive musculus (“little mouse”). The word and spelling “muscle” denote contractile tissue, and the movement under a muscle, especially under the bicep, can be compared to that of a mouse moving about under something. 🦪💪🐭

Resources

--

--