Погружение в автотестирование на iOS. Часть 3. Жизненый цикл iOS приложения во время прогона тестов

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

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

В этой статье я расскажу про жизненый цикл iOS приложения во время прогона тестов, а в частности про:

  • Предусловия и постусловия в ui-тестах;

  • Запуск/завершение работы приложения;

  • Запуск стороних приложений;

  • Сброс permissions;

  • Определение состояния приложения.

Предусловия и постусловия в ui-тестах

В классе с автотестами мы можем кофигурировать предусловия и постусловия используя следующие методы:

  • Переопределить метод класса setUp(), чтобы установить предусловия для всех тестов в классе(выполняется перед первым тестом в классе);

  • Переопределить метод setUpWithError(), чтобы установить предусловия и обработать ошибки перед запуском каждого теста;

  • Переопределить метод setUp(), чтобы установить предусловия перед запуском каждого теста в классе;

  • Объявить addTearDownBlock(_:) внутри своего тесты, чтобы выполнить очистку необходимых условий в рамках прохождения теста;

  • Переопределить метод tearDown(), чтобы выполнить очистку после каждого теста в клаcсе

  • Переопределить метод tearDownWithError(), чтобы выполнить очистку после каждого теста в клаcсе и обработать ошибки;

  • Переопределить метод класса tearDown(), чтобы выполнить очистку после того как все тесты в классе завершаться.

Пример объявления и использования методов отображен в коде ниже:

//Source: developer.apple.com

class SetUpAndTearDownExampleTestCase: XCTestCase {
    
    override class func setUp() { // 1.
        // This is the setUp() class method.
        // It is called before the first test method begins.
        // Set up any overall initial state here.
    }
    
    override func setUpWithError() throws { // 2.
        // This is the setUpWithError() instance method.
        // It is called before each test method begins.
        // Set up any per-test state here.
    }
    
    override func setUp() { // 3.
        // This is the setUp() instance method.
        // It is called before each test method begins.
        // Use setUpWithError() to set up any per-test state,
        // unless you have legacy tests using setUp().
    }
    
    func testMethod1() throws { // 4.
        // This is the first test method.
        // Your testing code goes here.
        addTeardownBlock { // 5.
            // Called when testMethod1() ends.
        }
    }
    
    func testMethod2() throws { // 6.
        // This is the second test method.
        // Your testing code goes here.
        addTeardownBlock { // 7.
            // Called when testMethod2() ends.
        }
        addTeardownBlock { // 8.
            // Called when testMethod2() ends.
        }
    }
    
    override func tearDown() { // 9.
        // This is the tearDown() instance method.
        // It is called after each test method completes.
        // Use tearDownWithError() for any per-test cleanup,
        // unless you have legacy tests using tearDown().
    }
    
    override func tearDownWithError() throws { // 10.
        // This is the tearDownWithError() instance method.
        // It is called after each test method completes.
        // Perform any per-test cleanup here.
    }
    
    override class func tearDown() { // 11.
        // This is the tearDown() class method.
        // It is called after all test methods complete.
        // Perform any overall cleanup here.
    }
    
}

На картинке ниже отображена последовательность выполнения методов:

Запуск/завершение работы приложения

Если мы хотим запустить/завершить приложение, мы можем легко сделать это с помощью метода запуска или завершения:

// Запускаем приложение
XCUIApplication().launch()
// Завершаем работу приложения
XCUIApplication().terminate()

Если мы хотим запустить приложение с определенными аргументами или переменными окружения, которые были настроены в основном приложении, мы можем запустить приложение с launchArgument или launchEnvironment. Более подробно об аргументах и переменных окружения можно почитать здесь.

let app = XCUIApplication()
// Добавляем аргумент для запуска приложения
app.launchArguments.append("-debugServer")
// Добавляем переменную окружения для запуска приложения
app.launchEnvironment = ["inAppPurchasesEnabled":"false",
                         "inAppAdsEnabled":"false"]
app.launch()

Также это можно сделать через тест-план. Во вкладке Configuration разделе Arguments будут два поля:

  • Arguments Passed on launch — сюда добавляем необходимые аргументы для запуска нужного состояния приложения;

  • Environment variables — сюда добавляем необходимые переменные окружения для запуска нужного состояния приложения.

Более подробно о тест-планах можно прочитать в этой статье.

Запуск стороних приложений

Бывают ситуации, когда нужно запустить стороннее приложение. Например, Safari для проверки диплинков. Это можно сделать в рамках прохождения теста или на старте приложения. Для этого нужно знать bundle id приложения и передать его в качестве аргумента в XCUIApplication.

Информация об доступных bundle id можно найти на сайте apple.

Пример кода для запуска приложения по bundle id:

// Запускаем safari
XCUIApplication(bundleIdentifier: "com.apple.mobilesafari").launch()

// Во время прохождения теста открываем safari
XCUIApplication(bundleIdentifier: "com.apple.mobilesafari").activate()

Сброс permissions

На WWDC 2020 Apple представили возможность в тестах сбрасывать permissions. Это удобная функция, если вам нужно протестировать разные состояния экрана, завязанные на permissions. Важно, что метод доступен на симуляторах с версией iOS выше или равно 13.4.

Список permissions для сброса, можно посмотреть в перечислении — XCUIProtectedResource:

@available(iOS 13.4, *)
public enum XCUIProtectedResource : Int {

    // All platforms
    case contacts = 1

    case calendar = 2

    case reminders = 3

    case photos = 4

    case microphone = 5

    case camera = 6

    case mediaLibrary = 7

    case homeKit = 8

    
    // macOS-specific resources
    
    // iOS Family-specific resources
    
    case bluetooth = -1073741824

    case keyboardNetwork = -1073741825

    case location = -1073741826
}

Пример как отключить permissions доступа к контактам в рамках теста:

override func setUpWithError() throws {
        if #available(iOS 13.4, *) {
            application.resetAuthorizationStatus(for: .contacts)
        } else {
            throw XCTSkip("Required API is not available for this test.")
        }
    }

В коде выше мы сбрасываем доступ к контактам в предусловии, если симулятор с версией iOS выше или равно 13.4. Если же нет, мы пропускам тест в прогоне.

Состояния приложения

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

У приложения есть несколько состояний, которые можно получить во время прохождения теста:

public enum State : UInt {
        
	case unknown = 0 // Текущее состояние приложения неизвестно

	case notRunning = 1 // Приложение не запущено

        
	case runningBackgroundSuspended = 2 // Приложение работает в фоновом режиме, но приостановлено

        
	case runningBackground = 3 // Приложение работает в фоновом режиме

	case runningForeground = 4 // Приложение работает 
}

Представим ситуацию, что нам нужно нажать на кнопку home на iPhone 5s и проверить, что приложение перешло в состояние foreground.

Пример реализации такого кейса:

// Нажимаем на текущем симуляторе кнопку home
XCUIDevice.shared.press(.home)
// Делаем явное ожидания, чтобы состояния приложение обновилось и мы перешли на экран home симулятора
Thread.sleep(forTimeInterval: 5)
// Проверяем что состояние приложения - runningForeground
XCTAssertEqual(XCUIApplication().state, .runningForeground)

Самое важное:

  • Вы можете конфигурировать предусловия и постусловия ваших тестов, используя методы setUp() и tearDown();

  • Запустить приложения, зная его bundle id;

  • Если в приложении есть аргументы или переменные окружения, их можно добавить перед прогоном тестов;

  • Сбрасывать состояния пермишенов вашего приложения можно на симуляторах версии iOS выше или равно 13.4.

В следующей заключительной статье по погружению в iOS-автоматизацию мы расскажем: что такое ожидания в ui-тестах, для чего они нужны, какие бывают, и приведем пример, как написать эффективные ожидания для ваших тестов.

Навигация по статьям:

  • Погружение в автотестирование на iOS. Часть 1. Как работать с accessibilityidentifier объектов

  • Погружение в автотестирование на iOS. Часть 2. Как взаимодействовать с ui-элементами iOS приложения в тестах

Источник: https://habr.com/ru/company/vivid_money/blog/544254/


Интересные статьи

Интересные статьи

Побег от алгоритма YouTube Я люблю смотреть видео на YouTube, осязаемым образом улучшающие мою жизнь. К сожалению, алгоритм YouTube с этим не согласен. Он любит кормить меня кликбэйт...
Если вы задавались вопросами производительности труда операторов и управления средним временем обработки контакта (Average Handling Time, AHT), то материал, который вы се...
На досуге мы с сыном изучаем цифровую элетронику. Недавно мы дошли до главы про конечные автоматы. На эту тему полно типичных задач, вроде семафора или торгового автомата. Но они все ...
Стремление уйти от ручного регрессионого тестирования — хороший повод внедрить автотесты. Вопрос, какие именно? Разработчики интерфейсов Наталья Стусь и Алексей Андросов вспомнили, как их команда...
В предыдущей статье я немного показал в работе самодельный сцинтилляционный радиометр. Прибор заинтересовал публику и в связи с этим выходит данная статья, описывающая радиометр изнутри. ...