June 6, 2022

Погружение в автотестирование на iOS. Часть 5. Взаимодействие с system alerts в ui-тестирование.

Привет, Хабр!
При работе с ui-тестами мы переодически сталкиваемся с системными алертами и нотификациями разного типа, которые нам приходится обрабатывать.
Картиники:

Наиболее расспространненый способ взаимодействия с ними - это кликать на нужные кнопки из кода теста или делать отдельный тест в наборе, который явно запускается первым и проходит все алерты при первом запуске приложения.

let springBoardApp = XCUIApplication(
    bundleIdentifier: CommonPage.AnotherAppIds.springBoard.rawValue
)

let app = XCUIApplication()

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

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

Второй способо это использовать системный обработчик addUIInterruptionMonitor.

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 в setUp() не будет отрабатывать автоматически на всех тестах и его нужно вызывать в начале теста и для каждого аллерта нужен отдельный обработчик. Так же addUIInterruptionMonitor плохо взаимодействует с такими алертами, как запрос трекинга активности пользователя.

Третий способ - это управление добавлением в инициализацию делегатов, отвечающих за алерты и нотификации.
Создадим перепенную, которую передается через launcharguments и реализуем обработчики в AppDelegate.

func launch(arguments: [String] = ["UI-TestingNotifications"]) {
    if state != .notRunning {
        terminate()
        launchArguments = []
    }

    launchArguments = arguments

    launch()
    _ = wait(for: .runningForeground, timeout: 10)
}

// In class AppDelegate
private lazy var appDelegates: [UIApplicationDelegate] = {
    if !ProcessInfo.processInfo
      .arguments.contains("UI-TestingNotifications") {
        appDelegates.append(pushNotificationsAppDelegate)
        appDelegates.append(appsFlyerAppDelegate)
    }
}


Таким способом мы сможем явно отключить все виды алертов и нотификаций в тестах и включать их только в нужных тестах, где они являются частью сценария.