Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I write a UI test that launches the app with a push notification payload and verifies that you are routed to the correct view?

I'm implementing push notifications in an iOS app, and as I'm doing so, I want to write a UI test that verifies that the app does the correct thing when it is launched with a certain push notification payload (i.e. the app navigates to the correct table view and highlights the correct cell).

Can this be done? I can't seem to find anyone who has done this before or has asked this question before.

Thankful for any pointers.

like image 708
vrutberg Avatar asked Feb 22 '17 16:02

vrutberg


People also ask

How do you run a UI test?

Go to File > New > Target. Then, search for UI Testing Bundle. Select Unit Testing Bundle and then click Next. As UI tests take time, it is usually best to stop immediately when a failure occurs.

What is push notification payload?

Push payloads are sent with encrypted content when the mobile app supplies an RSA public encryption key upon push registration with the Salesforce push notification service. When a payload is sent from a customer org to a user's device, the mobile app processes and decrypts the payload.


1 Answers

With Xcode 9 you can now actually test Remote Notification handling in a UITest. I implemented that using a framework called NWPusher

I wrote a long blogpost about my implementation and added a demo project to github.

Here is a short description of what I did:

Preparation

  1. Add NWPusher to your UITest target (I used Carthage)
  2. Download a APN Development Certificate for your app from Apple's Dev Center
  3. Open that certificate in Keychain and export it as p12 file
  4. Add this file to the IUTest target
  5. Make the deviceToken available to the UITestRunner

Write the Test

The test does the following steps:

  1. Create a reference to the app and the Springboard
  2. Launch the app and close it by tapping the home button (dismiss the system dialog asking for permission if it pops up)
  3. Trigger a remote notification (using NWPusher)
  4. Query the remote notification banner from the Springboard and tap it
  5. Test if the remote notifications has been handled correctly by your app
  6. Close the app and test the next type of remote notification

In my demo the different types of notifications trigger differently colored modal View Controller in the app. So my test class looks like this

import XCTest
import PusherKit

class PushNotificationUITests: XCTestCase {

    override func setUp() {
        super.setUp()
        continueAfterFailure = false
    }

    func testPushNotifications() {
        let app = XCUIApplication()
        app.launchArguments.append("isRunningUITests")
        let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")

        app.launch()

        // dismiss the system dialog if it pops up
        allowPushNotificationsIfNeeded()

        // get the current deviceToken from the app
        let deviceToken = app.staticTexts.element(matching: .any, identifier: "tokenLabel").label

        // close app
        XCUIDevice.shared.press(XCUIDevice.Button.home)
        sleep(1)

        // trigger red Push Notification
        triggerPushNotification(
            withPayload: "{\"aps\":{\"alert\":\"Hello Red\"}, \"vcType\":\"red\"}",
            deviceToken: deviceToken)

        // tap on the notification when it is received
        springboard.otherElements["PUSHNOTIFICATION, now, Hello Red"].tap()

        // check if the red view controller is shown
        XCTAssert(app.staticTexts["Red"].exists)

        // dismiss modal view controller and close app
        app.buttons["Close"].tap()
        XCUIDevice.shared.press(XCUIDevice.Button.home)
        sleep(1)

        // trigger green Push Notification
        triggerPushNotification(
            withPayload: "{\"aps\":{\"alert\":\"Hello Green\"}, \"vcType\":\"green\"}",
            deviceToken: deviceToken)

        // tap on the notification when it is received
        springboard.otherElements["PUSHNOTIFICATION, now, Hello Green"].tap()

        // check if the green view controller is shown
        XCTAssert(app.staticTexts["Green"].exists)

        // dismiss modal view controller and close app
        app.buttons["Close"].tap()
        XCUIDevice.shared.press(XCUIDevice.Button.home)
        sleep(1)

        // trigger blue Push Notification
        triggerPushNotification(
            withPayload: "{\"aps\":{\"alert\":\"Hello Blue\"}, \"vcType\":\"blue\"}",
            deviceToken: deviceToken)

        // tap on the notification when it is received
        springboard.otherElements["PUSHNOTIFICATION, now, Hello Blue"].tap()

        // check if the blue view controller is shown
        XCTAssert(app.staticTexts["Blue"].exists)

        // dismiss modal view controller 
        app.buttons["Close"].tap()
    }
}

extension XCTestCase {
    func triggerPushNotification(withPayload payload: String, deviceToken: String) {
        let uiTestBundle = Bundle(for: PushNotificationUITests.self)
        guard let url = uiTestBundle.url(forResource: "pusher.p12", withExtension: nil) else { return }

        do {
            let data = try Data(contentsOf: url)
            let pusher = try NWPusher.connect(withPKCS12Data: data, password: "pusher", environment: .auto)
            try pusher.pushPayload(payload, token: deviceToken, identifier: UInt(arc4random_uniform(UInt32(999))))
        } catch {
            print(error)
        }
    }

    func allowPushNotificationsIfNeeded() {
        addUIInterruptionMonitor(withDescription: "“RemoteNotification” Would Like to Send You Notifications") { (alerts) -> Bool in
            if(alerts.buttons["Allow"].exists){
                alerts.buttons["Allow"].tap();
            }
            return true;
        }
        XCUIApplication().tap()
    }
}

This only works on a physical device, because remote notifications do not work in the simulator.

like image 140
joern Avatar answered Nov 14 '22 23:11

joern