В предыдущей статье мы познакомились с AirTest IDE, но, на всякий случай, давайте повторим: AirTest IDE разработан компанией NetEase и предназначен для "hard-to-automate" приложений, таких как, например, игр. Собственно на них и делается основной упор разработчиками, хотя это не мешает использовать AirTest и для любых других приложений.


Данная работа является второй в цикле, посвящённому AirTest IDE. Первую, обзорную, статью про AirTest IDE вы можете найти здесь, а третью и последнюю, которая посвящена фреймворку UI автоматизации Poco — по данной ссылке.


Сегодня же я расскажу вам об одном из 2х основных фреймворков — AirTest. AirTest — это кросс-платформенный фреймворк для автоматизации UI, основанный на принципах распознавания изображений (Image Recognition), который, как заявляют разработчики, подходит для игр и приложений. AirTest Project на GitHub содержит 4 проекта: Airtest, Poco, iOS-Tangent, multi-device-runner.


А теперь давайте перейдём к самому интересному!


image


Как работает AirTest


AirTest обрабатывает полученные скриншоты на основе процесса thresholding. Суть заключается в том, чтобы сравнить интенсивность пикселей на изображении с некоторым числом (threshold value) и, если значение пикселя больше, то назначить ему цвет (чаще всего используется белый). В противном случае назначается другой цвет — чёрный. Как итог, на выходе получаем черно-белое изображение. Из этого следует естественное ограничение — AirTest не учитывает цвет во время распознавания. К примеру, если у вас используется объект с одним и тем же силуэтом, но разной, к примеру, раскраской и вам нужно проверить наличие элемента именно с определенной цветовой палитрой, то это будет крайне затруднительно, а может и не сработает вовсе.


К примеру в рамках пробы AirTest IDE было решено поработать с игрой Marvel Puzzle Quest. При загрузке персонажи с данной комикс вселенной мелькают на экране в определенном порядке. Время от времени у них меняются костюмы и это впервые насторожило меня. В примере ниже я ожидал, в рамках теста, Человека Паука в классическом, а получил в стелс костюме. Тест, собственно, прошёл успешно, а произошло это по причине описанной выше — использование чёрно-белого изображения во время распознавания. Пример того, что ожидалось и что, в итоге, получилось:


image


Улучшение вероятности успешного завершения тестов


Как вы уже поняли, Image Recognition — это далеко не панацея, хоть и работает здесь он хорошо. Для написания качественных тестов вам не избежать написания кода и, соответственно, знания основ Python. К примеру прежде чем искать конкретный элемент, неплохо было бы убедиться, что он действительно есть на экране. Бывают случаи, когда AirTest "промахивается" и может принять неправильный элемент за тот, что нужен вам. Время от времени бывают проблемы с распознаванием текста, который вы хотите найти при помощи Image Recognition. AirTest может спутать результаты и считать, что нужный вам текст есть на экране, но, на самом деле, текст там совершенно другой. Процесс анализа результатов призвана упростить система генерации отчётов, которая уже встроена в AirTest IDE. Создать и открыть отчёт можно после завершения теста/скрипта при помощи сочетания клавиш Ctrl/Cmd+L.


По общим рекомендациям я бы выделил ещё и следующее.


  • Делайте скриншоты именно тех элементов, которые вам нужны. Я имею в виду, что если вам нужна кнопка, которая, к примеру, находится на сложном текстурном заднике, то постарайтесь сделать скрин только кнопки, чтобы не тратилось время на обработку задника, который, по сути, вам не нужен. В таком случае поиск не будет зависеть от того, что изображено на заднике и вы получите более точный результат за меньшее время.
  • Постарайтесь избегать распознавания изображений на которых присутствует только текст, т.к. успешное распознавание ("success rate") в таком случае будет сильно сниже��о.
  • Хоть создание скриншотов во время автоматической записи кода (script auto recording) является достаточно удобной функцией, но, местами, скриншоты создаются не очень информативными. Лучше создавать их вручную, чтобы хранить в ваших картинках больше полезной информации для поиска.

И если вы хотите спросить "Возможно ли изменить настройки процесса поиска изображений?", то я вам отвечу — да, это возможно.


Настройки поиска изображений (Image Recognition)


Пользователю позволено и рекомендуется работать с настройками Image Recognition, чтобы добиться нужных результатов, оптимизировать время и вероятность успешного распознавания элементов (success rate) на экране. Эти настройки хранятся в окне Image Editor и, чтобы открыть его, вам нужно дважды кликнуть на нужное изображение в Script Editor. Настройки распознавания каждой картинки нужно менять отдельно либо использовать глобальные переменные, если, к примеру, вы хотите увеличить требования к точности threshold операции для вашего проекта.


Окно Image Editor

image


Image Editor содержит в себе рабочую зону, а также "Snapshot+Recognition" и "Show Help" кнопки. Первая отвечает за сравнение вашей текущей картинки со snapshot версией. Snapshot картинка захватывается с текущего окна на вашем девайсе. Вторая кнопка открывает мануал по функционалу Image Editor. В правой части окна отображается текущая картинка для поиска, а также такие настройки как filename, threshold, target_pos и rgb.


  • filename поле отвечает за имя текущего сохраненного изображения (все картинки сохраняются в папке проекта).
  • threshold хранит в себе значение процента совпадения (от 0 до 1) изображений после распознавания. Чем выше значение, тем выше требование к точности сопоставления изображений. Как упоминалось выше, AirTest преобразует изображения в оттенки черно-белого (в зависимости от реализации threshold) и, следовательно, цвет не учитывается во время распознавания.
  • rgb чекбокс призван "включить и добавить" цвет во время распознавания изображений и, тем самым, начать учитывать его. Однако учитывайте, что включение данного функционала всё равно не гарантирует 100% результата. К примеру, если у вас есть 2 одинаковых кнопки, которые различаются только цветом заднего фона, вероятность некорректного распознавания (например в рамках assert_exists/assert_not_exists) будет достаточно велика.
  • target_pos отвечает за точку на картинке на которую AirTest нажмет после распознавания. По умолчанию значение равно 5, но вы можете изменять его от 1 до 9, где 1 — это левый верхний угол вашего рабочего изображения, а 9 — правый нижний угол. Местоположение всех девяти точек наглядно изображено на скриншоте ниже. Также вы можете почитать об этом в официальной документации
    В данном примере для распознавания выделена средняя кнопка. Контур показывает границы выделения.

image


Написание автоматизированных тестов при помощи AirTest


Все рабочие команды фреймворка AirTestможно найти в окне AirTestAssistant в левом верхнем углу Airtest IDE. Если его нету, то можете выставить расположение окон по умолчанию при помощи Window -> Default layout.


Местоположение окна AirTest Assistant

image


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


  1. touch — данная команда имитирует touch жест на мобильном устройстве. Touch имеет следующие параметры — touch(v, times=1, duration=0.01, right_click=False).
    • v — картинка или координата (x,y)
    • times — количество нажатий. По умолчанию значение равно 1
    • duration — продолжительность удержания после касания экрана. При помощи этого параметра можно имитировать"долгое нажатие" (long_touch). Значение по умолчанию равно 0.01 секунды
    • right_click — нажатие "правой кнопки мыши". Может быть использовано только в для Windows программ
  2. wait — ожидание UI элемента. Команда имеет следующие параметры — wait(v, timeout=TIMEOUT, interval=5, intervalfunc=None).
    • v — изображение, которое ожидает программа
    • timeout — время ожидания. Значение по умолчанию равняется 20
    • intervalfunc — пользовательская (кастомная) функция. Если изображение не найдено, то будет выполнена эта функция
    • interval — интервал между сравнением изображений
      Функция возвращает следующее: если изображение найдено, тогда возвращаются координаты центра данного изображения, иначе выбрасывается TargetNotFoundError
  3. swipe — данная команда имитирует swipe жест на мобильном устройстве ("проведения пальцем по экрану"). Swipe имеет следующие параметры: swipe(v1, v2=None, vector=None, duration=0.01).
    • v1 — значение с которого начинается свайп. Может быть как изображением, так и заданной координатой (x, y)
    • v2: значение окончания свайпа (команда выполняется с v1 до v2). У данного параметра приоритет выше, чем у параметра "vector"
    • vector[x,y] — создается во время работы AirTest или же можете задать его самостоятельно. Указывает в какую сторону нужно свайпнуть. Чтобы свайпнуть вправо, X должен быть положительным, в свою очередь Y должен иметь положительное значение для свайпа вниз.
    • steps — не пользовался данным параметром и не нашел как он применяется на практике. Плашка с подсказкой выдаёт следующее: "the node in the swipe path, default 5". Предположу, что вектор направления свайпа будет разделен на "секции" и вместо того, чтобы свайпнуть мгновенно из точки А в точку Б, будет сымитирован свайп с небольшими паузами в нодах, указанных в данном параметре, как бы имитируя поэтапное перемещение. К примеру если значение равно 5, значит вектор будет разделен на 5 отрезков.
    • duration — длительность свайпа. Значение по умолчанию равно 0.5 секунды
  4. exists — проверка существует ожидаемый вами элемент на экране монитора устройства. exists имеет следующие параметры: exists(v)
    • v — изображение
      Функция возвращает следующее: если изображение найдено, тогда возвращаются координаты центра данного изображения, иначе возвращается False.
  5. text — команда ввода текста. text имеет следующие параметры: text(text, enter=True, search=False)
    • text — текстовая строка, которую нужно ввести
    • enter — данный параметр отвечает за то, нужно ли использовать "Enter" после ввода текста. Значение по умолчанию — True
    • search — не пользовался данным параметром и не нашел как он применяется на практике. Плашка с подсказкой выдаёт следующее: "force "Search" or not after input". Значение по умолчанию — False
  6. keyevent — эмулирование нажатия физических кнопок на устройстве, таких как HOME, BACK, MENU, POWER и т.д. Параметры для данной команды: keyevent(keyname)
    • keyname — название кнопки (POWER, HOME и т.д.)
  7. snapshot — создание скриншота экрана в текущем состоянии. Параметры по умолчанию: snapshot(filename=None, msg="test-point")
    • filename — сохранить текущий скриншот как отдельный файл. Можно проигнорировать данный параметр.
    • msg — описание данного тестового поинта. Этот текст будет отображен в HTML отчете, который можно создать после окончания теста.
      Данная функция возвращает следующее: имя файла (filename).
  8. sleep — запущенный тест "засыпает" на некоторое время. Значения по умолчанию: sleep(secs=1.0)
    • secs — время ожидания. Значение по умолчанию равно 1 секунде.
  9. assert_exists — проверка, существует ли элемент. Параметры для данной команды: assert_exists(v, msg="test-point")
    • v — изображение с элементом, наличие которого проверяется
    • msg — описание данного тестового поинта. Этот текст будет отображен в HTML отчете, который можно создать после окончания теста
      Данная функция возвращает следующее: если изображение найдено, то возвращаются координаты центральной точки данного изображения, инач�� выпадает AssertionError
  10. assert_not_exists — проверка, что элемент не присутствует на экране устройства. Параметры для данной команды: assert_not_exists(v, msg="test-point")
    • v — изображение с элементом, наличие которого проверяется
    • msg — описание данного тестового поинта. Этот текст будет отображен в HTML отчете, который можно создать после окончания теста
  11. assert_equal — проверка, что атрибут равен заданному значению. Параметры для данной команды: assert_equal(first, second, msg="test-point")
    • first — первый элемент для сравнения
    • second — второй элемент для сравнения
    • msg — описание данного тестового поинта. Этот текст будет отображен в HTML отчете, который можно создать после окончания теста
  12. assert_not_equal — проверка, что атрибут не равен заданному значению. Параметры для данной команды: assert_not_equal(first, second, msg="test-point")
    • first — первый элемент для сравнения
    • second — второй элемент для сравнения
    • msg — описание данного тестового поинта. Этот текст будет отображен в HTML отчете, который можно создать после окончания теста

Данные команды разделены на 3 основные группы: Операции (Operations), вспомогательные функции (Auxiliary functions) и проверки (Assertions). Вы можете выбрать нужную вам группу при помощи соответствующего фильтра (выпадающее меню сразу под названием окна).


Фильтр групп команд

image


Команды, которым необходимо изображение, активируют функции записи скрин��ота сразу после нажатия на соответствующую кнопку. К примеру, чтобы выбрать на какой элемент нажать на экране, выберите команду touch в AirTest Assistant и в окне Device Screen, на активном устройстве, обведите нужный для нажатия элемент. После этого в главном окне (Script Editor) появится соответствующая команда, в нашем случае touch, с изображением в качестве параметра. Как итог, процесс автоматизации выглядит следующим образом (гифка записана с устаревшей версии AirTest IDE):


image


Если вы по какой-то причине не хотите вручную создавать скриншоты и\или в целом писать код, то можете воспользоваться функцией автоматической записи. Активировать её можно при помощи нажатии на кнопку "камеры" напротив выпадающего меню с группами команд в окне Airtest Assistant. Автозапись — штука достаточно точная и удобная, но, само-собой, не является панацеей и не заменит ручного набора кода.


image


Стоит упомянуть ещё 3 горячие клавиши — F5 (запуск скрипта), F10 (остановка запущенного скрипта), Ctrl+L/Cmd+L (создание отчёта на основе законченного теста).
Запускать готовые тесты можно и без UI, используя терминал (command line). Больше информации об этом в целом и о запуске тестов в частности можно найти здесь.


Пример отрывка теста, написанного при помощи AirTest фреймворка можно найти под спойлером!


Пример автотеста написанного при помощи AirTest (Image Recognition)

image


Само-собой UI в вашем приложении/игре не состоит сплошь и рядом из уникальных иконок, кнопок, задников и т.п. Плюс ко всему периодически визуально идентичные элементы могут встречаться на одном экране, например кнопки, слайдеры и т.д. Чаще всего в таких случаях AirTest не сможет распознать нужный вам элемент и, либо тест упадет с ошибкой, либо будет выбран неверный элемент интерфейса для дальнейших манипуляций.


Пример окна с несколькими идентичными элементами

image


Специально для таких случаев был разработан ещё один фреймворк, который уже встроен в AirTest IDE. Он Poco и вкратце о нём уже было рассказано в статье с общим обзором Airtest IDE. Подробнее же о данном фреймворке я расскажу в следующей статье.


Расскажите, пользовались ли вы уже AirTest IDE и что вы думаете о данном инструменте. Буду рад обсуждению в комментариях!