Подробнее о Sikuli в автоматизации тестирования

Вступление


Sikuli — это API позволяющая писать на Jython сценарии автоматизации опираясь на визуальную составляющую любой программы/сайта и т.д. Особенно приятна для автоматизации Flash.
О Sikuli написано мало статей и большинство из них обзорные. Ещё меньше русскоязычного хелпа, и ещё меньше примеров кода. И отсутствие последнего пожалуй самое трагичное для тестировщика ПО который столкнулся в работе с необходимостью автоматизировать какой либо флэш. Как раз это и подтолкнуло меня написать более ёмкую статью по Sikuli и описать несколько подробнее некоторые особенности использования.


Установка


Для установки и правильной работы Sikuli вам понадобится:
1. Java шестой версии и обязательно 32х разрядная.
2. Дистрибутив
3. Пакет обновлений.

В связи с тем, что Sikuli идет только в 32-разрядной комплектации, то для правильной работы на 64х-битных системах в обязательном порядке ставим Java 6u38 32х (с 7й версией не работает).
Так же необходимо поставить пакет обновлений. Устанавливается легко. Просто копируется из архива (пункт 3) содержимое папки «Sikuli-IDE» в папку с установленной программой «C:\Program Files (x86)\Sikuli X», кстати не меняйте папку установки назначенную по умолчанию.
Если вдруг что не так, вот ссылочка для почитать.

Функционал и особенности.


Снимок экрана image
Применяется для выделения определённой области экрана и последующем созданием паттерна image
При нажатии на паттерн появится окно «Настройка шаблонов» image
С вкладками всё примитивно и ясно. Единственное отмечу что на вкладке «смещение цели» целью является то место куда переместиться курсор и произведет клик/ввод. Причём место клика может лежать далеко за пределами паттерна.

Теперь о особенностях.
*****.png это название искомого рисунка который должен лежать в папке заранее сохраненного проекта. image
similar = схожесть, где 1=100% (лучше не использовать ибо находит не всегда), а например 0,85 (через точку) = 85%, что на мой взгляд самое оптимальное значение.
targetOffset(x,y)) и тут важно что координаты «x» и «y» это смещение цели от центра паттерна.

Выделение области/wait()/Click()

image
Великолепная функция и что самое главное в крайней степени полезная.
Многие говорят что Sikuli плохо соображает в работе и ошибается ещё здорово. Я же с этим в корне не согласен и считаю, что грамотный подход значительно увеличивает производительность программы по сравнению с аналогичными IDE.

Эта функция задает активную область экрана.
image
в которой будет производиться поиск искомого паттерна.
Можно ещё и вот так:
Region(x,y,w,h).find(Pattern("*****.png".similar(0.85)targetOffset(x,y)))

Она крайне полезна если нам нужно чтобы скрипт реагировал на изменения оперативно, не тормозил систему.
Как пример:
из всего нашего большого экрана мы выбираем только небольшую область где каждый раз появляется заветная кнопочка. image
Далее описываем что в этой области нам надо искать и что с этим потом делать.
image
Для того чтобы оптимизировать процесс нам нужно запустить этот скрипт и чтоб он за нас 1 раз совершил клик. После мы переходим на вкладку «Сообщение» где видим координаты клика.
image
Далее делаем цикл и и делаем клик по координатам (это при условии что кнопка всегда будет появляться в одном и том же месте).
Схожесть в поиске делаем около 70 процентов, быстрее найдёт, ожидание 999…
image
По итогу мы имеем практически моментальное срабатывание.


Find()

Функция find() ищет паттерн. Но вот зачем? Всё просто. Один раз нашел, а дальше он будет знать где находится найденный объект всегда. Как применить на практике:
Имеем много чего либо… image
И нажимать это «что либо» программе придется долго и упорно…
Для того чтобы программа знала куда нажимать, нужно выставить targetOffset()
image
Повторяем для каждой цели, присваиваем переменные, оптимизируем если поле с множеством находится в одном и том же месте.
разворачивать тут
image

Далее мы можем найденные зоны использовать как хотим. Причём клик будет происходить без задержек на поиск и т.д.
image

Остальной функционал рассматривать не буду он примитивен и прост.

Примеры


Сценарий должен быть вменяемый!
image

Создание функции yan(). Первое обращение к функции вызовет команду find(), последующие обращения вызовут click().
image

Через диалоговое окно задаем количество циклов.
image

функция try / except и какая она хорошая.
image

И в завершение бот по отсылке подарков друзьям (коих может быть 5к) в игре DOTD
Settings.MoveMouseDelay = 0.02

def f5():
    while "f5":
        click(Pattern("KyddinsmapsD-1.png").targetOffset(-41,0))
        sleep(5)
        if exists(Pattern("1344731699750.png").similar(0.90),10):
            continue
        else:
            pass
        try:
            wait(Pattern("CLOSElah.png").similar(0.85),25)
        except:
            continue
        try:
            click(Pattern("CLOSElah.png").similar(0.85).targetOffset(1,-6))
            dragDrop(Location(1430,388), Location(1431,435)) # 435
            break
        except:
            pass
def send_frend():
    click(Location(849,495))
    sleep(1)
    click(Location(578,695))
    sleep(1)

def pre_click_frend():
    click(Location(713,300))
    click(Location(713,315))

def click_frend():
    click(Location(713,332))
    click(Location(714,348))
    click(Location(714,364))
    click(Location(715,380))
    click(Location(714,396))
    click(Location(713,412))
    click(Location(713,427))
    click(Location(713,444))
    click(Location(713,460))
    click(Location(713,474))
    click(Location(713,490))
    click(Location(715,506))
    click(Location(714,522))
    click(Location(714,537))
    click(Location(713,554))
    
    click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), 
    click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), 
    click(Location(923,541)), click(Location(923,541))
    
    sleep(1)
    click(Location(713,288))
    click(Location(713,304))
    click(Location(713,320))
    click(Location(713,336))
    click(Location(713,352))
    click(Location(713,368))
    click(Location(713,384))
    click(Location(713,400))
    click(Location(713,416))
    click(Location(713,432))
    click(Location(713,448))
    click(Location(713,464))
    click(Location(713,480))
    click(Location(713,496))
    click(Location(713,512))
    click(Location(713,528))
    click(Location(713,544))
    
    click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), 
    click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), click(Location(923,541)), 
    click(Location(923,541))
    
    sleep(1)
    click(Location(713,300)),
    click(Location(713,315))
    click(Location(713,332))
    click(Location(714,348))
    click(Location(714,364))
    click(Location(715,380))
    click(Location(714,396))
    click(Location(713,412))
    click(Location(713,427))
    click(Location(713,444))
    click(Location(713,460))
    click(Location(713,474))
    click(Location(713,490))
    click(Location(715,506))
    click(Location(714,522))
    click(Location(714,537))
    sleep(1)

def send_fb():
    click(Location(756,760))
    sleep(2)
    try:
        click(Pattern("0TIIp3BHTb33.png").targetOffset(-2,1))
    except:
        pass

def off (event):
    
    popup("The program is completed.\nCreator of this miracle: Grumo Van Blum.\nIf you have questions about using this program, please contact us by e-mail: grumovanblum@gmail.com\nGood Luck 8c)")
    exit()


popup("If you want to stop the script, press: 'Ctrl' + 'Alt' + 'Space'")

Env.addHotkey(Key.SPACE, KeyModifier.ALT+KeyModifier.CTRL, off)

click(Pattern("1344836792562.png").similar(0.88))
n = 0
n = int(input("How many cycles you want to run?"))



while n > 0:
    f5()
    while "cycle":
        if n == 0:
            break
        else:
            
            send_frend()
            if exists(Pattern("Jd.png").similar(0.96),1):
                popup("Friends come to an end :)")
                n=0
                continue
            else:
                pre_click_frend()
                sleep(1)
                if exists(Pattern("Youcanontysd.png").similar(0.95),1):
                    f5()
                    send_frend()
                    pre_click_frend()
                else:
                    pass
            click_frend()
            send_fb()
            n -= 1
            if n == 0:
                break
            else:
                sleep(3)



И видео о том как он работает (снимал в режиме отладки).


Используемые полезности


Alt+Shift+c остановит исполнение скрипта и вернёт IDE.

Выставляем время перемещения курсора:
Settings.MoveMouseDelay = 0.02  

Присваиваем сочетаниям клавиш какое либо событие:
def off (event):
    exit()
Env.addHotkey(Key.SPACE, KeyModifier.ALT+KeyModifier.CTRL, off)

Вызов диалогового окна (целочисленного):
n = int(input("А сколько раз будем проверять?"))

Бездействие в 1 сек:
sleep(1)

Если у вас после вставки скопированного текста код паттерна или региона заменился на картинку просто нажмите 1 или 2 раза сочетание клавиш Ctrl+z.
image

Это ещё далеко не всё и будет обязательно ещё. Но на данном этапе считаю, что основную идею я выразил.
Буду рад фидбэку.
Если есть вопросы по Sikuli, то пишите.

Следующим разом скорее всего расскажу как Sikuli научить распознавать текст и записывать лог ошибок в отдельный файл.

Спасибо за внимание.
  • +12
  • 48,4k
  • 9
Поделиться публикацией

Комментарии 9

    0
    Ну и название!
      +1
      К сожалению, название это не худшая её часть.

      Все работает Очень медленно. IDE, написанная на
      Java добавляет своих особенностей… тестировать десктопные
      приложения, наверное можно. Браузер — лучше сразу застрелиться.

      По личным ощущениям, лучше использовать не эту IDE,
      а велосипедировать на питоне,
      управляя мышкой\клавой,
      снимая и анализируя скриншоты самостоятельно.
      Гемора примерно столько же — гибкости значительно больше.

      Ну и это true way, в конце концов, более похож на программирование,
      нежели на создание макросов, чем по-сути и является использование этой IDE.
      Вероятно, тем кто знает Java\Groovy\Clojure\Scala — будет легче расширять саму систему,
      делая её более гибкой, нежели писать велосипед,
      на том же питоне(любом_другом_языке).
        0
        Браузер — лучше сразу застрелиться.

        Для web'a же есть прекрасный Selenium!
          0
          Есть и не менее прекрасный PhantomJS(и, опционально — CasperJS).
            +1
            Кстати, в PhantomJS 1.8 была добавленна поддержка вебдрайвера (на основе ghostdriver).
          0
          И, к сожалению, давно не разрабатывается.
            +1
            Согласен с вышесказанным.
            Эта статья написана скорее для тех людей которые столкнулись с необходимостью автоматизации в сложных ситуациях.
            Один из примеров: в регресстесте игры приходилось сравнивать отображаемые данные с табличными. Причём отображаемые данные были текстово-числовые но отображались в виде картинки без возможности их выдрать (точнее возможность была, но человеческий фактор играет свою роль). И плюс к тому это безобразие было динамическим. Вот и пришлось взять в руки Sikuli привинтить распознавание текста и научить сравнивать с данными из Excel-ника.
            Единственное по поводу скорости это я маху дал. Сейас поправлю. Я имел в виду среди своих сородичей.
          0
          Может, конечно, что-то изменилось, но я сам очень интересовался возможностью использовать Sikuli для тестирования GUI, но стабильность самого инструмента просто никакая. Простейшие сценарии ломались за обозримое время (или жрали море ресурсов / начинали находить вообще не похожие на искомые изображения объекты).
          Короче, Вы сколько-нибудь длительные тесты запускали?
            0
            Запускал 1-3 дневные но для того чтоб они стабильно работали мне их пришлось очень хорошо катать, чтобы на все изменения она реагировала адекватно а не вылетала с ошибкой сценария. А вот ошибки распознования они бывают и самое интересное выскакивают на анимации. Редно но бывает. И в основном из-за того, что распознаётся маленький объект. Нюансов выше крыши. А вот ресурсы кушает старательно это факт, но мне 2х ядер хватает. Если остались исходники я бы на них с радостью взглянул t3950339()gmail.com

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое