Как стать автором
Обновить

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

Время на прочтение6 мин
Количество просмотров68K

Вступление


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 научить распознавать текст и записывать лог ошибок в отдельный файл.

Спасибо за внимание.
Теги:
Хабы:
Всего голосов 14: ↑13 и ↓1+12
Комментарии9

Публикации

Истории

Работа

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань