Взаимодействие с alerts и permissions в ui-тестировании iOS +1


Привет, Хабр!

Меня зовут Ахмат. Я iOS QA Automation Engineer - в Vivid Money.

В этой статье я хочу рассказать про взаимодействие с alerts и permissions в iOS тестах и показать, как их можно эффективно обрабатывать в своём проекте.

Данная статья будет полезна начинающим iOS-автоматизаторам, либо разработчикам, которые решили изучить XCUITest и покрыть свой проект ui-тестами.

В рамках статьи мы разберем:

  • Что такое alerts и permissions вашего iOS приложения;

  • Как обрабатывать alerts в коде теста, где они являются частью сценария;

  • Как автоматически обрабатывать permissions;

  • Как реализовать механизм включения и выключения alerts и permissions с помощью передачи аргументов при запуске тестов.

Что такое alerts и permissions?

Alerts - дают возможность предоставлять пользователям критически важную информацию.
Например, alert может сообщать пользователю, что приложение запрашивает разрешение на отправку им нотификаций, и давать им возможность подтвердить или отклонить. Alerts появляются при первичном запуске вашего приложения.

Permissions - позволяют приложениям запрашивать разрешение на использование настроек конфиденциальности. Например таких, как Location Services для определения вашего точного местоположения.

Permissions появляются при первичном входе в раздел, к которому требуется запросить у вас разрешение.

Как обрабатывать alerts?

Способы взаимодействия с alerts - сделать их частью сценария (онбординг/авторизация и т.д) и кликать на нужные кнопки из кода теста или делать отдельный тест в наборе, который явно запускается первым и проходит все alerts при первом запуске приложения.

Alerts находятся в иерархии текущего активного приложения, иногда это ваше тестируемое, иногда это springboard (встроенное приложение, которое управляет домашним экраном iOS).

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

let app = XCUIApplication()

if springBoardApp.alerts.buttons["Allow"].waitForExistence(timeout: 1) {
		springBoardApp.alerts.buttons["Allow"].tap()
} else if app.alerts.buttons.element(boundBy: 1).waitForExistence(timeout: 1) {
		app.alerts.buttons["Allow"].tap()
}

Наиболее эффективный способ - это обрабатывать alerts, когда они являются частью сценария.

Создание отдельного теста для обработки всех alerts при первичном запуске может привести к нестабильности прогона всего тестового набора. Зачастую может произойти сбой во время прокликивания alerts, что в последствии приведет к частичному падению следующих тестов в наборе.

Как автоматически обрабатывать permissions?

В тестовый проект нужно добавить монитор прерывания пользовательского интерфейса. В XCTest есть метод, addUIInterruptionMonitor() который мы можем использовать для мониторинга и реагирования на появление permissions.

addUIInterruptionMonitor(withDescription: "Tracking Usage Permission Alert") {
    (alert) -> Bool in
    if alert.buttons["Allow"].exists {
        alert.buttons["Allow"].tap()
        self.app.activate()
        return true
    }
    return false    
}

Наиболее эффективно вызывать addUIInterruptionMonitor() в начале теста, где мы точно знаем что по сценарию появится permission. Вызов обработчика в setUp() не будет отрабатывать автоматически на всех тестах. Так же для каждого permission нужен отдельный вызов монитора прерывания пользовательского интерфейса.

Как включать и выключать alerts и permissions с помощью передачи аргументов?

Можно включать появление alerts и permissions только в тех тестах, где они будут являться частью тестового сценария.

Потребуется реализовать механизм управления добавлением в инициализацию делегатов, отвечающих за alerts и permissions.

Реализуем механизм включения/выключения оповещения о трекинге активности. За появление диалогового окна трекинг менеджера в нашем приложении отвечает механизм ATTrackingManager.

Создадим расширение для XCUIApplication, где напишем функцию launch с возможностью передачи аргументов:

extension XCUIApplication {
	
	func launch(arguments: [String] = ["TrackingManager"]) {
	    if state != .notRunning {
	        terminate()
	        launchArguments = []
	    }
	
	    launchArguments = arguments
	
	    launch()
	    _ = wait(for: .runningForeground, timeout: 10)
	}

}

Далее нам потребуется выяснить в каком делегате у нас реализован вызов диалогового окна трекинг менеджера. Достаточно в поиске ввести ATTrackingManager и взять название делегата.

final class AppsFlyerAppDelegate: NSObject, UIApplicationDelegate {

    func applicationDidBecomeActive(_ application: UIApplication) {
        // for iOS 13 and below - The IDFA will be collected by the SDK. The user will NOT be prompted for permission.
        if #available(iOS 14, *) {
            // Show the user the Apple IDFA consent dialog (AppTrackingTransparency)
            // Can be called in any place
            ATTrackingManager.requestTrackingAuthorization { _ in
            }
        }
        
        if let deviceId = CheckEmailAccessExperiment.obtainDeviceId(inPlace: "AppsFlyerInstance") {
            appsFlyerInstance.customerUserID = deviceId
        }
        
        appsFlyerInstance.start()
    }
}

Реализуем обработчик в AppDelegate и запуск ATTrackingManager в тестах, где аргумент “TrackingManager” не передается:

public class AppDelegate: UIResponder {
    
    let pushNotificationsAppDelegate = PushNotificationsAppDelegate()
    let appsFlyerAppDelegate = AppsFlyerAppDelegate()

    private lazy var appDelegates: [UIApplicationDelegate] = {

        var appDelegates: [UIApplicationDelegate] = [
            pushNotificationsAppDelegate
        ]
        
        if !ProcessInfo.processInfo.arguments.contains("TrackingManager") {
            appDelegates.append(appsFlyerAppDelegate)
        }
        
        return appDelegates
    }()
    
}

Таким способом можно отключать все виды alerts и permissions по отдельности, как показано в примере выше или можно создать один общий аргумент для обработки добавления делегатов в appDelegates, отвечающий за все виды alerts и permissions.

Сочетая все три способа взаимодействия с alerts и permissions, можно сделать ваш код чистым и уменьшить число флаков при прогоне тесового набора.




Комментарии (0):