Xcode 7 UI Testing: Dismiss Push and Location alerts

后端 未结 4 1729
生来不讨喜
生来不讨喜 2020-12-09 08:26

I encountered a problem with Xcode 7 UI Testing.

The app displays two alerts after my user logs in, the Request Location Alert and the Push

相关标签:
4条回答
  • 2020-12-09 08:28

    While not ideal, I found that if you simply wait until one authorization dialog has finished before presenting another one in the app, UI tests can pick up multiple requests in a row.

        if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse || CLLocationManager.authorizationStatus() == .AuthorizedAlways {
            self.locationManager.requestLocation()
        } else {
            self.contactStore.requestAccessForEntityType(.Contacts) { _ in
                self.locationManager.requestWhenInUseAuthorization()
            }
        }
    

    I'm actually requesting access to contacts in a different place in my code, but it can handle multiple simultaneous requests just fine.

    Then in my test:

        addUIInterruptionMonitorWithDescription("Location Dialog") { (alert) -> Bool in
            let button = alert.buttons["Allow"]
            if button.exists {
                button.tap()
                return true
            }
            return false
        }
        addUIInterruptionMonitorWithDescription("Contacts Dialog") { (alert) -> Bool in
            let button = alert.buttons["OK"]
            if button.exists {
                button.tap()
                return true
            }
            return false
        }
    
        app.buttons["Location"].tap()
    
        app.tap() // need to interact with the app for the handler to fire
        app.tap() // need to interact with the app for the handler to fire
    
    0 讨论(0)
  • 2020-12-09 08:28

    To dismiss system alert views (ie: Push Notification) you can define custom flags for the testing environment.

    Then, you just have to change the application's code to avoid specific initialisations (ie: Push Notification) :

    #if !TESTING
    let settings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
    application.registerUserNotificationSettings(settings)
    #endif
    

    I use this trick in order to be able to take screenshots with snapshot.

    0 讨论(0)
  • 2020-12-09 08:43
    class BaseTest: XCTestCase {
        let pushSent = NSNotification.Name.init("alert.pushSent")
        var notificationMonitor: NSObjectProtocol?
    
        override func setUp() {
            listenNotifications()
            let app = XCUIApplication()
    
            notificationMonitor = addUIInterruptionMonitor(withDescription: "Push Notifications") { [unowned self] (alert) -> Bool in
                let btnAllow = app.buttons["Allow"]
                //1:
                if btnAllow.exists {
                    btnAllow.tap()
                    NotificationCenter.default.post(name: self.pushSent, object: nil)
                    return true
                }
                //2:
                //takeScreenshot
                XCTFail("Unexpected System Alert")
                return false
            }
            //3:
            //add code for "Request Location" monitor
    
            app.launchEnvironment = ["UITEST_DISABLE_ANIMATIONS" : "YES"]
            //4:
            app.launch()
    
        }
    
        func listenNotifications() {
            NotificationCenter.default.addObserver(forName: pushSent, object: nil, queue: nil) { (notification) in
                if let locationDialogHandeler = self.notificationMonitor {
                    //5:
                    self.removeUIInterruptionMonitor(locationDialogHandeler)
                }
            }
        }
    }
    

    1: Check if you're in the correct alert, tap the button and find a way to remove the monitor (I'm using NotificationCenter)

    2: If you enter a monitor and can not find the right button, it means it's an unexpected flow. Fail the test (but take a screenshot first).

    3: Add other monitors

    4: I am adding monitor even before launching the app. If you add a monitor after the alert appears, it will not be triggered.

    5: Remove the monitor, that way when a new alert appears, the next monitor in the stack will be called.

    P.S: You should add monitors in reverse order, therefore, add "Request Location" after "Push Notifications"

    0 讨论(0)
  • 2020-12-09 08:47

    As I noted in the answer you mentioned, you must interact with the application after the alert appears.

    Second, after presenting the alert you must interact with the interface. Simply tapping the app works just fine, but is required.

    // add UI interruption handlers
    
    app.buttons["Request Location"].tap()
    app.tap() // need to interact with the app for the handler to fire
    
    0 讨论(0)
提交回复
热议问题