Общие сведения

ВступлениеПодготовка к запускуАрхитектура платформы TestoПорядок запускаПолитика запуска тестов

Обучающие материалы по Testo для Hyper-V

Часть 1. Самый первый тестЧасть 2. Устанавливаем Ubuntu ServerЧасть 3. Доступ в Интернет из виртуальной машиныЧасть 4. Гостевые дополненияЧасть 5. ПараметрыЧасть 6. КешированиеЧасть 7. Связываем две машины по сетиЧасть 8. ФлешкиЧасть 9. МакросыЧасть 10. Конструкция ifЧасть 11. No snapshotsЧасть 12. Управление мышкойЧасть 13. Импортирование жёстких дисковЧасть 14. JS-селекторыЧасть 15. Циклы

Обучающие материалы по Testo для QEMU

Часть 1. Самый первый тестЧасть 2. Устанавливаем Ubuntu ServerЧасть 3. Гостевые дополненияЧасть 4. ПараметрыЧасть 5. КешированиеЧасть 6. Доступ в Интернет из виртуальной машиныЧасть 7. Связываем две машины по сетиЧасть 8. ФлешкиЧасть 9. МакросыЧасть 10. Конструкция ifЧасть 11. No snapshotsЧасть 12. Управление мышкойЧасть 13. Импортирование жёстких дисковЧасть 14. JS-селекторыЧасть 15. ЦиклыЧасть 16. Макросы с объявлениями

Спецификация языка

Общая структура скриптовых файловБазовые конструкции языкаOбъявление виртуальной машиныОбъявление виртуального флеш-накопителяОбъявление виртуальной сетиПараметрыОбъявление тестовМакросыДействия с виртуальными машинамиДействия с мышкойПоиск изображений на экранеДействия с виртуальными флеш-накопителямиУсловияЦиклыСписок идентификаторов клавиш

Запросы на языке Javascript

Общая концепция построения JS-селекторовВстроенные глобальные функции JavascriptИсключенияКласс TextTensorКласс ImgTensorКласс Point

Часть 15. Циклы

С чем Вы познакомитесь

В этом уроке вы познакомитесь с циклами в языке Testo-lang.

Начальные условия

  1. Платформа Testo установлена.
  2. Гипервизор Hyper-V установлен.
  3. На хостовой машине имеется доступ в Интернет
  4. Имеется установочный образ Ubuntu server 16.04 с расположением C:\iso\ubuntu_server.iso. Местоположение и название установочного файла может быть другим, в этом случае нужно будет соответствующим образом поправить параметр ISO_DIR, передаваемый через командную строку во время запуска тестов.
  5. (Рекомендовано) Настроена подсветка синтаксиса Testo-lang в Sublime Text 3.
  6. (Рекомендовано) Проделаны шаги из четырнадцатой части

Вступление

Наш цикл ознакомительных уроков с платформой Тесто плавно подходит к своему завершению, и в языке Testo-lang осталась лишь одна возможность, с которой мы ещё не познакомились - это циклы.

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

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

Например, давайте посмотрим на уже знакомый нам экран выбора языка при установке Ubuntu Server:

Language

Конечно, мы исходили из того, что нам требуется английский язык, и поэтому просто нажимали клавишу Enter, т.к. этот пункт уже выделен по-умолчанию. Но что, если нам требуется другой пункт, например "Русский"? Для этого нам надо 29 раз нажать на клавишу Down, чтобы для начала выделить эту надпись. А что, если мы не хотим считать нужное количество нажатий на клавишу Down? Если мы хотим просто вызвать макрос select_menu("Русский") и не беспокоиться о том, сколько раз для этого надо нажимать клавишу вниз? В языке Testo-lang Вы можете создавать такие макросы, и в этом Вам помогут циклы, о которых мы сегодня и поговорим.

С чего начать?

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

Для начала давайте почистим наши тесты от всего ненужного, оставим только одну виртуальную машину, установку ОС и гостевых дополнений для неё, а также добавим установку менеджера Vifm из репозиртория Ubuntu. В последнем тесте мы будем автоматизировать работу с менеджером Vifm, а пока ограничимся только его запуском.

test server_install_ubuntu {
    server install_ubuntu("${server_hostname}", "${server_login}")
}

test server_prepare: server_install_ubuntu {
    server {
        install_guest_additions("${server_hostname}", "${server_login}")
        exec bash "apt install -y vifm"
    }
}

test cycles_demo: server_prepare {
    server {
        type "vifm /"; press Enter
        abort "stop here"
    }
}

После выполнения этого теста мы увидим интерфейс менеджера Vifm:

Vifm

Давайте представим, что нам необходимо зайти в директорию /usr/sbin. Для этого нам нужно сначала 19 раз нажать "Down", затем Enter, затем снова 6 раз Down, и наконец снова Enter. Запишем это в сценарий:

test cycles_demo: server_prepare {
    server {
        type "vifm /"; press Enter
        sleep 2s

        press Down*19, Enter
        press Down*6, Enter
        abort "stop here"
    }
}

Такой скрипт действительно приводит нас к нужному результату:

usr_sbin

Но выглядит это всё не очень здорово, не так ли? Здесь есть две глобальные проблемы:

  1. Скрипт абсолютно плохо читается - тяжело понять, куда мы хотели бы попасть после того, как спустились вниз на 19 пунктов, а затем снова на 6.
  2. Каждый раз высчитывать точное количество нажатий на клавиши вниз-вверх - задача практически невыполнимая, да и бесмыссленная.

На самом деле, решить проблему №1 можно, вспомнив предыдущий урок, посвященный JS-селекторам (в частности, определению цвета фона у надписей). В самом деле, мы можем каждый раз осуществлять проверку, что мы выбираем именно тот пункт меню, который нам действительно нужен. Для этого лишь надо формализовать понятие "Выделенный пункт меню". В случае с Vifm выделенная строка отличается от любой другой надписи синим фоном, поэтому давайте вспомним предыдущий урок и модифицируем наш простой тест:

macro vifm_select(menu_entry) {
    if (check js "find_text('${menu_entry}').match_background('blue').size() == 1") {
        press Enter
    } else {
        abort "Menu entry ${menu_entry} is not selected"
    }
}

test cycles_demo: server_prepare {
    server {
        type "vifm /"; press Enter
        sleep 2s

        press Down*19; vifm_select("usr")
        press Down*6;  vifm_select("sbin")
        abort "stop here"
    }
}

Теперь наш тест выглядит уже немного более читаемым (теперь мы хотя бы можем понять, какой пункт меню мы выбираем), но у нас всё еще остается проблема с тем, что нам придется каждый раз высчитывать точное количество перещёлкиваний Вниз-Вверх чтобы выделить нужный пункт меню. Эту проблему можно исправить с помощью цикла.

Цикл для выбора пункта меню

Давайте модифицируем наш макрос vifm_select так, чтобы он выбирал нужный пункт меню "за нас". То есть автоматически определял количество нажатий на клавишу "Вниз", необходимое для перемещения на нужную строчку. Мы построим алгоритм следующим образом:

  1. Сначала надо переместить "указатель" на самый верхний пункт. Для этого в Vifm существует комбинация клавиш gg;
  2. Теперь мы будем в цикле последовательно проверять, выделен ли нужный пункт меню;
  3. Если нужный пункт не выделен, необходимо переместиться на одну позицию вниз и снова произвести проверку;
  4. Если нужный пункт выделен, мы нажимаем на клавишу Enter и выходим из цикла;
  5. Если мы израсходовали количество попыток на поиск нужного пункта, мы высвечиваем сообщение об ошибке, что нужного пункта меню не существует.

В коде это будет выглядеть следующим образом

macro vifm_select(menu_entry) {
    press g*2

    for (i IN RANGE 0 50) {
        if (check js "find_text('${menu_entry}').match_background('blue').size() == 1") {
            print "Got to entry ${menu_entry} in ${i} steps"
            press Enter
            break
        }
        press Down
        sleep 50ms
    } else {
        abort "Menu entry ${menu_entry} doesn't exist!"
    }
}

Давайте разберем синтаксический формат цикла for. Мы видим, что в заголовке используется выражение i IN RANGE 0 50. Это выражение означает, что тело цикла необходимо выполнить 50 раз (левая граница RANGE включается в отрезок, а правая - нет). i выступает счетчиком, к которому можно обращаться внутри цикла. Мы воспользовались этой возможностью чтобы вывести вспомогательную информацию на консоль (с помощью действия print), за сколько шагов мы смогли добраться до нужного пункта меню.

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

Мы также вставили небольшой sleep после нажатия на Down чтобы дать немного времени графическому интерфейсу отреагировать на наше воздействие.

Обратите внимание на секцию else для цикла. Подобно некоторым языкам, в Testo-lang в циклах можно писать секцию else, которая сработает в том случае, если прошли все запланированные итерации цикла. В нашем случае все пройденные операции цикла означают, что нужный пункт меню так и не был найден.

В том случае, когда нижняя граница для счетчика равна нулю, можно сделать запись немного более компактной: i IN RANGE 50

Границы RANGE также можно задавать, используя строки - так можно создавать параметризируемые циклы. Подробности см. здесь

В платформе Testo отсутствует возможность создавать бесконечные циклы, т.к. это противоречит самой природе тестирования: любые тесты обязательно должны рано или поздно закончиться

Давайте теперь вернемся к самому тесту:

test cycles_demo: server_prepare {
    server {
        type "vifm /"; press Enter
        sleep 2s
        vifm_select("usr")
        vifm_select("sbin")
        abort "stop here"
    }
}

Мы видим, что он стал намного компактнее и выразительнее. Теперь для навигации внутри Vifm нам достаточно просто писать вызовы макроса vifm_select, не заботясь о том, какое количество раз нужно нажимать клавиши "Вверх" - "Вниз" для того, чтобы добраться до необходимого пункта.

Если запустить наш тест, то вот что мы увидим:

C:\Users\Testo> testo run ./tests.testo --stop_on_fail --param ISO_DIR C:\iso --test_spec cycles_demo
UP-TO-DATE TESTS:
server_install_ubuntu
server_prepare
server_install_guest_additions
server_install_vifm
TESTS TO RUN:
cycles_demo
[ 80%] Preparing the environment for test cycles_demo
[ 80%] Restoring snapshot server_install_vifm for virtual machine server
[ 80%] Running test cycles_demo
[ 80%] Typing "vifm /" with interval 30ms in virtual machine server
[ 80%] Pressing key ENTER in virtual machine server
[ 80%] Sleeping in virtual machine server for 2s
[ 80%] Calling macro vifm_select(menu_entry="usr") in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key G 2 times in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('usr').match_background('blue').size() == 1" in virtual machine server
[ 80%] server: Got to entry usr in 19 steps
[ 80%] Pressing key ENTER in virtual machine server
[ 80%] Calling macro vifm_select(menu_entry="sbin") in virtual machine server
[ 80%] Checking "find_text().match('sbin').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key G 2 times in virtual machine server
[ 80%] Checking "find_text().match('sbin').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('sbin').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('sbin').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('sbin').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('sbin').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('sbin').match_background('blue').size() == 1" in virtual machine server
[ 80%] Pressing key DOWN in virtual machine server
[ 80%] Checking "find_text().match('sbin').match_background('blue').size() == 1" in virtual machine server
[ 80%] server: Got to entry sbin in 6 steps
[ 80%] Pressing key ENTER in virtual machine server
C:/Users/Testo/tests.testo:50:3: Caught abort action on virtual machine server with message: stop here

C:/Users/Testo/declarations.testo:15:1: note: the virtual machine server was declared here

[100%] Test cycles_demo FAILED in 0h:0m:37s
C:\Users\Testo>

Вот, собственно, и все, что Вам необходимо знать о циклах в Testo-lang.

Итоги

Циклы в языке Testo-lang могут существенно упростить написание тестов, связанных с неопределенным заранее количеством действий. Самое главное - грамотно определить условие того, что нужное событие наступило, а также правильно выбрать инкрементальные шаги, которые помогут Вам достичь этого события.

Готовые скрипты можно найти здесь