Всем привет!

Меня зовут Борис. Я AQA iOS-engineer в Vivid Money.

Это вступительная статья, в цикле статей по iOS-автоматизации, в которых хочется рассказать о пользе ui-тестов на проекте, их эффективном использовании и написании.

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

В рамках этой статьи, мы разберем такие этапы:

  1. Что такое ui-тесты и какую пользу они приносят.

  2. Установка и настройка окружения.

  3. Нахождение ui-элементов в проекте и проставление им accessibilityidentifier.

Что такое UI-тесты?

Когда мы слышим слово 'тестирование', мы обычно представляем проверку приложения по тест-кейсам. А что если можно не проходить тест-кейсы на регрессе самим, а доверить это рутинное дело машине?

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

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

Код, который вы пишете, имитирует действия пользователя с приложением, заставляя приложение отрабатывать так, как будто вы сами совершаете эти действия на своем телефоне. И в ходе этих действий мы можем проверять интересующие нас условия.

Какая польза от UI-тестов?

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

Написание UI тестов имеет множество преимуществ, особенно в тех случаях, когда приложение разрастается, и его все сложнее поддерживать.

Какие преимущества есть:

  • Тесты выполняются быстрее, чем их проходит manual QA;

  • Тесты можно запускать каждую ночь и получать отчет о качестве приложения к моменту выхода на работу утром;

  • Тесты не подвержены человеческому фактору и не упустят даже самую малозаметную деталь;

  • Берегут нервы вашего тестировщика на регрессе — ему не приходится проходить тест-кейсы по 100500 раз;

  • Сокращают время проведения регрессионного тестирования.

Выглядит все конечно классно, но неужели нет минусов, спросите вы? Они есть и весьма весомые:

  • Пользовательский интерфейс может меняться, и ваши тесты могут устареть;

  • Участники команды должны уметь писать ui-тесты;

  • Тесты требуют обслуживания;

  • Достаточно хрупкие по сравнению с unit-тестами (если изменится положение кнопки — ui-тест может не пройти);

  • Также достаточно долго проходят по сравнению с unit-тестами (unit-тесты проходят меньше чем за секунду, ui-тесты будут запускать только приложение секунд 5).

Несмотря на эти недостатки, я считаю, что если у вас крупный проект, ui-тесты имеют право на существование.

Настраиваем среду:

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

Если у вас уже есть проект, то для добавления таргета с ui-тестами вам понадобится:

  1. Нажать на File > New > Target.

  2. Выбрать UI Testing Bundle.

  3. Нажать на Next и ввести название.

  4. Нажать Finish.

Xcode создаст новый таргет с пустым UITest файлом. Он вам понадобится для написания и запуска тестов.

Мы подготовили проект и разобрали, что за зверь такой, эти ui тесты. Теперь мы готовы разобрать пожалуй одну из базовых вещей в автотестах — это Accessibilityidentifier или как говорят в обиходе айдишник.

Что такое Accessibilityidentifier?

Accessibilityidentifier — Это строка идентефецирующая ваш ui-элемент.

Accessibilityidentifier позволяет найти нужный вам ui-элемент при выполнении автотеста.

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

Проставление id Объектам

Рассмотрим несколько способов проставления accessibilityidentifier объектам в зависимости от реализации проекта.

Проект, реализованный через storyboard

  1. Выбрать storyboard.

  2. Развернуть the Utilities.

  3. Выбрать Identity Inspector.

  4. Выбрать ваш объект на storyboard.

  5. Активировать Enabled.

  6. Ввести название identifier (в примере: button).

Проект, реализованный через SwiftUI

  1. Выбрать нужный View.

  2. Добавить метод .accessibility(identifier: "nameOfID") к нужному объекту.

Проект, реализованный через код

  1. Выбрать нужный ViewController.

  2. Присвоить accessibilityidentifier в viewDidLoad к необходимому объекту.

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var button: UIButton!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        button.accessibilityIdentifier = "button"
    }

}

Нахождение id Объектов

Мы разобрались, как присвоить accessibilityidentifier объектам, теперь рассмотрим несколько способов, как найти нужный id объекта.

Accessibility Inspector

Нативный инструмент Xcode. Он позволяет вам определить, есть ли у нужного объекта id без запуска самого теста.

Для его использования вам необходимо:

  1. Собрать приложение на симуляторе и запустить его.

  2. Нажать Xcode.

  3. Нажать Open Developer Tools.

  4. Нажать Accesibility Inspector.

  5. В Accessibility Inspector выбрать ваш симулятор с запущенным приложением.

  6. Нажать на значок в виде мишени.

  7. Тапнуть на интересующий элемент в приложении.

Дальше отобразится информация о выделенном объекте:

Basic

  1. Label — Текстовое значение, которое находится в выделенном объекте.

  2. Value — Значение, которое находится в выделенном объекте. Например, заполненый текст в TextFields.

  3. Traits — Название объкта (Например: Button, Static Text).

  4. Identifier — Название id выделенного объекта.

Actions:

Здесь отображаются действия, которые возможно выполнить с объектом. Это может быть свайп, нажатие и другие.

Element:

Class — Класс выделенного объекта.

Address — Адрес объекта в памяти.

Controller — View Controller, в котором находится объект.

Hierarchy:

Раздел в котором отображается иерархия объектов на экране. Этот раздел удобен когда не получается выделить этот объект курсором и можно в него провалиться по иерархии чтобы узнать id.

Console (lldb)

Для использования этого способа понадобится поставить брейкпоинт в вашем тестовом сценарии и в момент выполнения, когда брейкпоинт сработает, написать в дебаг консоль:

po XCUIApplication().debugDescription

Где po — print object команда, чтобы вывести информацию об объекте.

XCUIApplication — класс, позволяющий запускать приложение и взаимодействовать с его ui элементами.

debugDescription — свойство, отображающие иерархию элементов на экране.

Attributes: Application, pid: 67835, label: 'UiTestssProjects'
Element subtree:
 >Application, 0x6000026290a0, pid: 67835, label: 'UiTestssProjects'
    Window (Main), 0x600002629180, {{0.0, 0.0}, {375.0, 667.0}}
      Other, 0x600002629260, {{0.0, 0.0}, {375.0, 667.0}}
        Other, 0x600002629340, {{0.0, 0.0}, {375.0, 667.0}}
          Other, 0x600002629420, {{0.0, 0.0}, {375.0, 667.0}}
            Button, 0x600002629500, {{40.0, 156.0}, {46.0, 30.0}}, label: 'Button'
              StaticText, 0x6000026295e0, {{40.0, 162.0}, {46.0, 18.0}}, label: 'Button'
            StaticText, 0x6000026296c0, {{144.0, 161.0}, {42.0, 21.0}}, label: 'Label'
            Button, 0x6000026297a0, {{40.0, 216.0}, {46.0, 30.0}}, identifier: 'ButtonID', label: 'Button'
              StaticText, 0x600002629880, {{40.0, 222.0}, {46.0, 18.0}}, label: 'Button'
            Switch, 0x600002629960, {{183.0, 245.0}, {51.0, 31.0}}, value: 1
            Button, 0x600002629a40, {{40.0, 268.0}, {46.0, 30.0}}, label: 'Button'
              StaticText, 0x600002629b20, {{40.0, 274.0}, {46.0, 18.0}}, label: 'Button'
            TextField, 0x600002629c00, {{133.0, 374.0}, {97.0, 34.0}}
    Window, 0x600002629ce0, {{0.0, 0.0}, {375.0, 667.0}}
      Other, 0x600002629dc0, {{0.0, 0.0}, {375.0, 667.0}}
        Other, 0x600002629ea0, {{0.0, 0.0}, {375.0, 667.0}}
Path to element:
 >Application, 0x6000026290a0, pid: 67835, label: 'UiTestssProjects'
Query chain:
 >Find: Target Application 'fox.UiTestssProjects'
  Output: {
    Application, pid: 67835, label: 'UiTestssProjects'
  }

В этой иерархии мы видим, что за элементы есть на экране, их координаты, текст в них, значение, и, самое главное, identifier.

Test Recorder

Test recorder — нативный инструмент от Apple, который пишет тестовые сценарии за вас. Вам лишь нужно запустить его и прокликать необходимые шаги на симуляторе в запущенном приложении.

Чтобы рекордер стал активным вам нужно:

  • UI test target(ссылка) в вашем проекте;

  • Тестовый класс с методом, который начинается с test.

Для использования этого способа достаточно в методе вашего теста нажать на record и нажать на нужный вам объект на симуляторе. Если у этого объекта есть id — он отобразится в сгенерированом коде, если нет — можно увидеть, как альтернативным способом обратиться к нужному объекту.

Заключение:

Ui-тесты — это классный инструмент для повышения качества вашего продукта, но он не всегда необходим. Чтобы понять целесообразность его применения в вашем проекте, можно обратиться к статьям, в которых приводятся конкретные моменты, когда нужно или не нужно это делать. Вот пример такой статьи.

В следующей статье, мы расскажем про то, какие бывают ui-элементы в iOS-приложении и как с ними взаимодействовать при написании ui-тестов.