如何编写使用推送通知有效负载启动应用程序的UI测试,并验证您是否已路由到正确的视图?

问题描述 投票:4回答:3

我正在iOS应用程序中实现推送通知,正如我所做的那样,我想编写一个UI测试,用于验证应用程序在使用某个推送通知负载启动时是否正确(即应用程序导航)到正确的表视图并突出显示正确的单元格)。

可以这样做吗?我似乎无法找到之前已经做过此事或曾经问过这个问题的人。

感谢任何指针。

ios swift push-notification xcode-ui-testing
3个回答
7
投票

使用Xcode 9,您现在可以在UITest中实际测试远程通知处理。我使用名为NWPusher的框架实现了它

我写了一篇关于我实现的长blogpost,并向github添加了一个演示项目。

以下是我所做的简短描述:

制备

  1. 将NWPusher添加到您的UITest目标(我使用了Carthage)
  2. 从Apple的开发中心下载适用于您的应用程序的APN开发证书
  3. 在Keychain中打开该证书并将其导出为p12文件
  4. 将此文件添加到IUTest目标
  5. 使deviceToken可用于UITestRunner

写下测试

该测试执行以下步骤:

  1. 创建对应用程序和Springboard的引用
  2. 启动应用程序并通过点击主页按钮将其关闭(如果弹出,请关闭系统对话框,询问是否允许)
  3. 触发远程通知(使用NWPusher)
  4. 从Springboard查询远程通知横幅并点击它
  5. 测试您的应用是否已正确处理远程通知
  6. 关闭应用程序并测试下一种类型的远程通知

在我的演示中,不同类型的通知会在应用程序中触发不同颜色的模态View Controller。所以我的测试类看起来像这样

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()
    }
}

这仅适用于物理设备,因为远程通知在模拟器中不起作用。


0
投票

您可以使用此library发送通知。不幸的是,您应该在AppDelegate类中添加与测试相关的代码。我在单独的应用程序目标(UITEST=1)中使用自定义预处理器宏。

你代码中的某个地方:

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // ...
    #if UITEST
        // setup listening
    #endif
}

0
投票

基于joern惊人的文章,我took a step forward并找到了一种以编程方式与收到的通知进行交互的方法,因为它被XCTest框架识别为XCUIElement。

我们可以获得对Springboard的引用

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

例如,当将应用程序放在后台时,我们可以获得对收到的通知的引用(当它显示在屏幕顶部时),如下所示:

let notification = springboard.otherElements["NotificationShortLookView"]

允许我们点按通知:

notification.tap()

将其拉下来查看其操作(如果有的话。通过这样做可以让我们看到Rich Notification的内容):

notification.swipeDown()

与其行动互动:

let action = springboard.buttons["ACTION BUTTON TITLE"]
action.tap()

甚至可以与文本输入通知操作进行交互(在示例中,通过其占位符获取对通知文本字段的引用,您可以在代码中定义):

let notificationTextfield = springboard.textFields["Placeholder"]
notificationTextfield.typeText("this is a test message")

最后,您还可以获取对通知的关闭按钮的引用以解除它:

let closeButton = springboard.buttons["Dismiss"]
closeButton.tap()

通过能够自动化这种交互,我们可以测试,例如分析,如article中所述。

© www.soinside.com 2019 - 2024. All rights reserved.