Основы мета-языка
- Описание тестов
- Селекторы
- Выражения и кванторы
- Кванторы и коллекции
- Проверка инвариантов
- Использование python кода в тестах
Для описания тестов платформа “Meta Test” использует специализированный мета-язык. По сути это декларативный, внутренний предметно-ориентированный язык предназначенный для описания автоматизированных QA тестов пользовательского интерфейса веб приложений.
Такой подход позволяет:
- Значительно сократить время описания тестов
- Повысить стабильность тестов
- Снизить порог вхождения в автоматизированное тестирование
- Получить эффективное многопоточное выполнение тестов (за счёт гарантированной атомарности на уровне теста)
Поскольку интерпретатор мета-языка работает поверх виртуальной машины python, то возможно комбинировать тесты описанные на мета-языке с программным кодом написанным на языке python. Но нужно учитывать что код на python-е будет работать только во время загрузки тестов, но не во время их выполнения.
Описание тестов
TEST[“Заголовок”](Последовательность команд , {атрибут=значение})({параметр=значение})
Этот объект используется для описания теста. Обязательно должен быть задан заголовок теста и последовательность команд которые будут выполнены при запуске теста. Для конкретизации свойств теста он может быть определён со следующими атрибутами. Если атрибут не задан явно, то будут использовано его значение по умолчанию или взято значение из параметров среды.
- tags - Множество текстовых тегов на которых определён тест. Если это множество не пересекается с множеством CONFIG.TAGS, то тест не будет запущен. Если множество пустое или аргумент явно не определён, то тест будет выполнен только в том случае если множество тегов CONFIG.TAGS не определено. Если имя тега начинается с символа **@*, то это означает что тест на этом теге не может быть определён. Например: если атрибут теги [“@Тег1”, “Тег2”], то тест не будет запущен если CONFIG.TAGS содержит тег “Тег1”. По умолчанию - {}*
- browser - Множество идентификаторов браузеров на которых определён тест. Если это множество не пересекается с множеством CONFIG.BROWSERS, то тест не будет запущен. Если множество пустое или аргумент явно не определён, то тест будет выполнен на всех браузерах из множества CONFIG.BROWSERS. По умолчанию - {}
- test_id - Идентификатор теста (см CONFIG.TEST_ID). Уникальная строка которая однозначно идентифицирует тест. Если аргумент не определён, то идентификатор генерируется автоматически.
- size - Размер окна браузера. Может быть определён как кортеж (длина, ширина) или как строковые константы. По умолчанию - CONFIG.PAGE_SIZE
Константа | Описание |
---|---|
maximize | Развернуть окно во весь экран |
fullscreen | Полноэкранный режим |
minimize | Свернуть окно браузера |
- fatality - Завершать тестирование после первой ошибки. По умолчанию - CONFIG.TEST_FATALITY
- retries - Устанавливает максимальное количество перезапусков теста. По умолчанию - CONFIG.RETRIES
- retries_sleep - Устанавливает задержку в сек. перед перезапуском теста. По умолчанию - CONFIG.RETRIES_SLEEP
- repiated - Устанавливает количество запусков теста (игнорируется если установлен retries). По умолчанию - CONFIG.TEST_REPIATED
- active - Флаг, если он установлен в False то тест не выполняется. По умолчанию - True
- single_thread - Флаг указывающий что все экземпляры теста (т.е. параметризованные тесты и тесты для разных браузеров) должны выполняться последовательно. По умолчанию - CONFIG.SINGLE_THREADS
- negative - Если этот флаг установлен в True то тест негативный. По умолчанию - False
- trigger - Идентификатор триггера для текущего теста.
- description - Текстовое описания теста.
- severity - Строковая константа устанавливающая степень важности тестируемого дефекта. Может принимать значения: TRIVIAL | MINOR | NORMAL | CRITICAL | BLOCKER. По умолчанию - NORMAL
- flaky - Флаг, если он установлен в True то тест считается не стабильным. Может использоваться для перезапуска только нестабильных тестов см. CONFIG.RETRIES_MODE. По умолчанию False
По мимо этих значений в расширениях могут быть описаны пользовательский атрибуты тестов (см. Интеграция с системами отчётов )
Пример:
"На хабр можно зайти"](
TEST["https://habr.com/")
URL("habr.com" > PAGE_URL
,
"Проверка наличия блока логотипа"
, "scope::habr/logo_locator")
, ONE(
"Проверка наличия блока поиска"
, "scope::habr/search_locator")
, ONE(
"Проверка наличия блока рекламы"
, "scope::habr/news_block_locator")
, ONE(
= "Мы можем зайти на habr.com, нам виден логотип и некоторые блоки"
, description = ("chrome", "firefox")
, browser = ("base", "habr")
, tags )
Параметризованные тесты
Тесты можно запускать с различными параметрами. Для каждого набора параметров будет запущен отдельный экземпляр теста. После запуска теста текущие параметры записываются в пространство имён теста и могут быть получены командой ARG.
Параметры теста могут быть именованные и не именованные. Если параметр именованный, то его имя явно задаётся при описания теста, в противном случае значение параметра можно получить по его индексу.
Пример:
from faker import Faker
= TEST["text-box"](
test "https://demoqa.com/text-box")
URL(
"Заполняем"
, "#userName", ARG(0))
, DATA("#userEmail", ARG(1))
, DATA("#currentAddress", ARG("address1"))
, DATA("#permanentAddress", ARG("address2"))
, DATA("#submit")
, CLICK(
"Проверяем"
, "#name") == "Name:" + ARG(0)
, ONE("#email") == "Email:" + ARG(1)
, ONE("p#currentAddress") == "Current Address :" + ARG("address1")
, ONE("p#permanentAddress") == "Permananet Address :" + ARG("address2")
, ONE(
= "form"
, tags
)
# выполняем тест "text-box" 100 раз.
# каждый раз генерируется новый набор тестовых данных
= Faker("ru_RU")
fake
for _ in range(100):
test(# Не именованные параметры
fake.name(), fake.email() =fake.address() # Именованные параметры
, address1=fake.address()
, address2 )
Если во время выполнения параметризованного теста могут меняться данные на сайте, то возможно будет целесообразным установить атрибут single_thread в True. Это позволит гарантировать последовательное выполнение тестов при многопоточном запуске что позволит обеспечить их независимость от изменения данных другим экземплятор теста.
Субтесты
Иногда необходимо не завершая текущий тест открыть новый браузер и выполнить ряд действий на тестируемом сайте. Для этих целей используются субтесты. Они определяются как вложенный тест без указания заголовка.
Пример:
"Основной тест"](
TEST[
URL
, ...
TEST(
URL
, ...
)
, ... )
Субтест имеет общее пространство имён с основным тестом и наследует все его атрибуты.
ARG(“имя” [, значение ] [, default=значение ])
Перед запуском теста создаётся специальное пространство имён доступ к которому могут получить только команды выполняемые во время тестирования. Это пространство используется для обмена данными между скриптами, субтестами и основным тестом, а так же для параметризации тестов и скриптов. Непосредственный доступ к пространству внутри теста можно получить посредством команды ARG следующим образом:
- ARG(“имя”, значение) - Устанавливает ассоциацию имени со значением.
- ARG(“имя”) - Возвращает значение ассоциированное с именем. Если с именем не было ассоциировано никаких данных, то действие в котором использован аргумент будет пропущено. В режиме отладки пропущенные действия отображаются со статусом SCIP. Если необходимо пропустить не одно, а несколько действий, то можно воспользоваться условным оператором.
- ARG(“имя”, default=value) - Возвращает значение ассоциированное с именем. Если с именем не было ассоциировано никаких данных, то возвращается значение определённое в атрибуте default.
Пример:
# этот блок будет выполнен только если определён аргумент user
"user"), (
IF(ARG("pht::Username", ARG("user"))
DATA("button")
, CLICK( ))
Обратите внимание на то что аргументы не являются переменными. Одна команда должна установить определить ассоциацию с данными лишь единожды.
SCRIPT[“идентификатор”](Последовательность команд, {атрибут=значение})
Этот объект позволяет задать идентификатор последовательности команд что позволяет переиспользовать её в тестах.
Для скрипта может быть переопределён атрибут fatality.
Пример:
# Создаём скрипт
"login"](
SCRIPT["/login")
URL("pht::Username", "Admin")
, DATA("pht::Password", "admin123")
, DATA("button")
, CLICK(
=True
, fatality )
SCRIPT[“идентификатор”] | SCRIPT(“идентификатор” {“идентификатор”}, {аргумент=значение})
Для того что бы выполнить ранее определённый скрипт нужно использовать команду SCRIPT[“Идентификатор”].
Пример:
"login"](
SCRIPT["/login")
URL("pht::Username", ARG("user"))
, DATA("pht::Password", ARG("passwd"))
, DATA("button")
, CLICK(~(PAGE_URL < "/login") # URL текущей страница не должен содержать подстроки /login
,
=True
, fatality
)
"Проверка входа на сайт"](
TEST["user", "Admin")
ARG("passwd", "admin123")
, ARG("login"]
, SCRIPT[ )
Как видно при большом количестве аргументов не удобно использовать их явное определение. По этому для вызова параметризованного скрипта нужно использовать конструкцию SCRIPT(“Идентификатор” {“идентификатор”}, {аргумент=значение}). Так же с её помощью возможно вызвать последовательно несколько скриптов указав их идентификаторы через запятую.
Пример:
"Проверка входа на сайт"](
TEST["login", user="Admin", passwd="admin123")
SCRIPT(
)
"Без пароля не войдём"](
TEST["login", user="Admin")
SCRIPT(=True
, negative
)
"Без логина не войдём"](
TEST["login", passwd="admin123")
SCRIPT(=True
, negative
)
"Заходим и выходим"](
TEST["login", "logout", passwd="admin123", user="Admin")
SCRIPT( )
STEP[“заголовок”](Последовательность команд, {атрибут=значение})
Логически связанную последовательность команд можно объединить вместе при помощи объекта STEP. Это позволит детализировать информацию выводимую в отчётах.
Для краткости можно указывать только заголовок шага, все команды находящиеся после него до следующего заголовка или конца теста будут рассматриваться как тело шага.
Пример:
..."Логин"](
, STEP["pht::Username", ARG("user"))
DATA("pht::Password", ARG("passwd"))
, DATA("button")
, CLICK(
)
# шаги могут быть вложенными:
..."Логин"](
, STEP["заполняем"](
STEP["pht::Username", ARG("user"))
DATA("pht::Password", ARG("passwd"))
, DATA(
)"button")
, CLICK(
)
# Сокращённая запись (вложенность не поддерживается)
..."Логин"
, "pht::Username", ARG("user"))
, DATA("pht::Password", ARG("passwd"))
, DATA("button") , CLICK(
Для шага могут быть определены атрибуты description, step_id и fatality.
SCOPE([“идентификатор”])
Для передачи данных между тестами можно создавать пользовательские пространства имён. Вызов объекта SCOPE возвращает ссылку на пространство имён доступное всем тестам. Это полезно для хранения данных которые используют тесты из разных файлов.
Объект SCOPE может принимать имя скопа которое является идентификатором возвращаемого пространства имён.
Пример:
from faker import Faker
= Faker("ru_RU")
fake
= SCOPE("user")
scp = fake.name()
scp.fio = fake.email()
scp.email = fake.address()
scp.address
"..."](
TEST[
..."#fio", scp.fio)
, DATA(
, ... )
Для доступа к SCOPE-пространству имён данным из теста кроме ссылки на само пространство можно использовать специальный локатор - “scope::space_id/name” или объект NP(“имя скопа”, “имя”)
Пример:
"#fio", "scope::scp/fio")
DATA(
"#fio", NP("scp", "fio")) DATA(
Селекторы
Для определения пути к элементу страницы используются селекторы. Существует несколько типов селекторов, обычно платформа может автоматически определит его тип. Если тип определяется не верно или необходимо использовать селектор другого типа, то его можно явно указать при помощи префикса селектора с разделителем “::”. Правила определения типа селектора приведены в таблице.
Префикс | Описание |
---|---|
/ | Поиск по XPATH |
( | Поиск по XPATH |
./ | Поиск по XPATH |
.. | Поиск по XPATH |
*/ | Поиск по XPATH |
. | Поиск по CSS селектору (класс) |
# | Поиск по CSS селектору (id) |
* | Поиск по CSS селектору |
[ … ] | Поиск по CSS селектору |
htlm-тег | Поиск по CSS селектору |
css:: | Поиск по CSS селектору |
xpath:: | Поиск по XPATH |
id:: | Поиск по атрибуту ID |
name:: | Поиск по имени |
class:: | Поиск по имени класса |
link:: | Поиск по полному тексту ссылки |
plink:: | Поиск по частичному тексту ссылки |
tag:: | Поиск по тегу |
pht:: | Поиск по тексту placeholder-а (через XPATH) |
text:: | Поиск по полному тексту элемента (через XPATH) |
Если ни одно из вышеприведённых правил не подходит, то происходит поиск элемента с видимым текстом соответствующим заданному селектору. Фактически поиск происходит по следующему XPATH селектору: //*[contains(normalize-space(text()), normalize-space(‘ТЕКСТ’))]
Селектор может указывать не на один, а на список элементов. В этом случае что бы выбрать конкретный элемент можно указать его индекс добавив его в конец префикса селектора - префикс[индекс]::селектор. Отсчёт индексов начинается с 1, индекс 0 будет указывать на последний элемент, индекс -1 на предпоследний и т.д.
# Кликнет на первый элемент который принадлежат классу react-datepicker-wrapper
"[1]::.react-datepicker-wrapper")
CLICK(
# То же, но с явным указанием типа селектора.
"css[1]::.react-datepicker-wrapper") CLICK(
Составные селекторы
Иногда бывает проще определить путь к элементу страницы как список селекторов подчинённых элементов. Такие селекторы называются составными. Этот способ определения селектора удобен ещё тем что позволяет комбинировать селекторы разных типов.
Пример:
# На форме есть несколько полей с заполнителем "Адрес".
# По этому выбираем то которое находится в контейнере с идентификатором "adress_1"
"#adress_1", "pht::Адрес"]) ONE([
Составными селекторами не стоит злоупотреблять т.к. скорость их работы может быть значительно меньше чем у обычных селекторов.
FP(“селектор фрейма” {“селектор фрейма”}, “селектор элемента” [, timeout ])
Для определения пути к элементу находящемуся в фрейме нужно использовать специальный объект FP (Frame Pointer). Сначала ему нужно передать селектор фрейма (или нескольких фреймов в порядке вложенности) в котором находится элемент, а затем селектор самого элемента.
timeout - Время явного ожидания элемента. По умолчанию - CONFIG.WAIT_TIMEOUT
Пример:
"nestedframes"](
TEST["https://demoqa.com/nestedframes")
URL("#frame1", "body")) == "Parent frame"
, ONE(FP("#frame1", "iframe", "body")) == "Child Iframe"
, ONE(FP( )
Этот способ удобен если нужно однократно обратится к элементу в фреме, если же нужно активно работать с такими элементами, то целесообразно полностью переключится на фрейм (см. описание команды FRAME)
SP(“селектор” {“селектор”}, “селектор элемента” [, timeout ])
Для элементов в теневом DOM тоже есть аналогичный объект, он называется SP (Shadow Pointer). Ему сначала передают селекторы контейнеров содержащих вложенный DOM, а затем селектор элемента.
timeout - Время явного ожидания элемента. По умолчанию - CONFIG.WAIT_TIMEOUT
Пример:
"shadow DOM"](
TEST["http://watir.com/examples/shadow_dom.html")
URL("#shadow_host", "input[type=text]"), "file.jpeg")
, DATA(SP("#shadow_host", "input[type=file]"), "file.jpeg")
, UPLOAD(SP("#shadow_host", "input[type=checkbox]"))
, CLICK(SP("#shadow_host", "#nested_shadow_host", "#nested_shadow_content")) == "nested text"
, ONE(SP( )
Выражения и кванторы со скалярными типами
Для проверки значений на страницы используются выражения. Выражения имеют смысл если являются предикатами т.е. возвращают логическое значение. Для проверки свойств веб-элементов используются объекты называемые кванторами они получили своё название из-за схожего поведения с одноимёнными абстракциями математической логики. Кроме кванторов в выражениях можно использовать константы, они подробно описаны в соответствующем разделе.
ONE(“селектор” [, propertie ] [, displayed ] [, strip ] [, timeout ])
Квантор единственности. Если селектор будет указывать более чем на один элемент или элемента вообще не будет обнаружено, то попытка выполнения квантора завершится с ошибкой. В противном случае квантор вернёт свойство элемента на который указывает селектор.
Какое именно свойство нужно вернуть указывается в необязательном атрибуте properties. Если атрибут не задан то:
- для элементов input, textarea, select, progress и param возвращается значение свойства value
- для элемента option возвращается значение свойства selected
- для всех других элементов значение свойства textContent
properties так же может ссылаться на css свойства элемента или на его атрибуты. Для этого нужно указать его префикс css:: или attr:: соответственно.
Кроме properties квантор может принимать следующие параметры:
- displayed - Если равен True, то ждём отображения элемента на страницы. По умолчанию - False
- strip - Если равен True, удаляются все пробельные символы в начале и конце возвращаемой строки. По умолчанию - True
- timeout - Время явного ожидания элемента. По умолчанию - CONFIG.WAIT_TIMEOUT
В выражениях с квантором ONE могут использоваться следующие операторы:
Оператор | Описание | Пояснение |
---|---|---|
== | Проверка на равенство | ONE(…) == значение |
!= | Проверка на неравенство | ONE(…) != значение |
@ | Проверка соответствия регулярному выражению | ONE(…) “@” r“выражение” |
>> | Вхождение подстроки без учёта регистра (Правоассоциативный) | “подстрока” >> ONE(…) |
<< | Вхождение подстроки без учёта регистра (Левоассоциативный) | ONE(…) << “подстрока” |
> | Вхождение подстроки с учётом регистра (Правоассоциативный) | “подстрока” > ONE(…) |
< | Вхождение подстроки с учётом регистра (Левоассоциативный) | ONE(…) < “подстрока” |
>= | Вхождение подстроки в начало значения | ONE(…) >= “подстрока” |
<= | Вхождение подстроки в конец значения | ONE(…) <= “подстрока” |
+ | Сумма числовых или конкатенация строковых значений | ONE(…) + значение |
- | Разность числовых значений | ONE(…) - значение |
* | Произведение числовых значений | ONE(…) * значение |
/ | Деление числовых значений | ONE(…) / значение |
// | Целочисленное деление числовых значений | ONE(…) // значение |
% | Остаток от деления числовых значений | ONE(…) % значение |
** | Возведение в степень числовых значений | ONE(…) ** значение |
[] | Получение значения по индексу | ONE(…)[индекс] |
& | Логическая операция И | (ONE(…) == знач1) & (ONE(…) == знач2) |
| | Логическая операция ИЛИ | (ONE(…) == знач1) | (ONE(…) == знач2) |
~ | Логическая операция НЕ | ~(ONE(…) == значение) |
Пример:
# Существует только ОДИН элемент соответствующий селектору "div.passwd"
"div.passwd")
ONE(
# Существует только ОДИН элемент соответствующий селектору "div.passwd"
# Текст которого содержит подстроку "Пароль"
"Пароль" > ONE("div.passwd")
# Существует только ОДИН элемент соответствующий селектору "div.passwd"
# Цвет фона которого равен #f5f5f5
"div.passwd", "css::background-color") == "#f5f5f5" ONE(
ALL(“селектор” [, properties ], [, displayed ] [, strip ] [, unique ] [, timeout ])
Квантор всеобщности Этот квантор возвращает не одно, а список свойств веб-элементов на которые указывает селектор. Его использование аналогично квантору ONE, за тем исключением что в выражение применяется не к одному значению, а их списку. Предикат с квантором ALL примет истину когда истину вернёт каждое значение переданное квантором.
Атрибуты:
- properties - Идентификатор возвращаемого свойства (см. описание квантора ONE)
- displayed - Если равен True, то ждём отображения элементов на страницы. По умолчанию - False
- strip - Если равен True, то удаляются все пробельные символы в начале и конце каждой возвращаемой строки. По умолчанию - True
- unique - Значения возвращаемые квантором будут уникальными (порядок значений сохраняется). По умолчанию - False
- timeout - Время явного ожидания ожидания элемента. По умолчанию - CONFIG.WAIT_TIMEOUT
Семантика операторов при операциях со скалярным значением:
Оператор | Описание | Пояснение |
---|---|---|
== | Проверка на равенство КАЖДОМУ значению выражения | ALL(…) == значение |
!= | Проверка на неравенство КАЖДОМУ значению выражения | ALL(…) != значение |
@ | Проверка соответствия КАЖДОГО значения регулярному выражению | ALL(…) “@” r“выражение” |
>> | Вхождение подстроки в КАЖДОЕ значение выражения без учёта регистра | “подстрока” >> ALL(…) |
<< | Вхождение подстроки в КАЖДОЕ значение выражения без учёта регистра | ALL(…) << “подстрока” |
> | Вхождение подстроки в КАЖДОЕ значение выражения с учётом регистра | “подстрока” > ALL(…) |
< | Вхождение подстроки в КАЖДОЕ значение выражения с учётом регистра | ALL(…) < “подстрока” |
>= | Вхождение подстроки в начало КАЖДОГО значения выражения | ALL(…) >= значение |
<= | Вхождение подстроки в начало КАЖДОГО значения выражения | ALL(…) <= значение |
+ | Сумма или конкатенация значения с каждым элементом выражения | ALL(…) + значение |
- | Разность значения с каждым элементом выражения | ALL(…) - значение |
* | Произведение значения с каждым элементом выражения | ALL(…) * значение |
/ | Деление значения с каждым элементом выражения | ALL(…) / значение |
// | Целочисленное деление значения с каждым элементом выражения | ALL(…) // значение |
% | Остаток от деления значения с каждым элементом выражения | ALL(…) % значение |
** | Возведение в степень значения с каждым элементом выражения | ALL(…) ** значение |
& | Логическая операция И над КАЖДЫМ элементом выражения | (ALL(…) == знач1) & (ALL(…) == знач2) |
| | Логическая операция ИЛИ над КАЖДЫМ элементом выражения | (ALL(…) == знач1) | (ALL(…) == знач2) |
~ | Логическая операция НЕ над КАЖДЫМ элементом выражения | ~(ALL(…) == значение) |
Пример:
# Текст во ВСЕХ элементов соответствующих селектору "div.passwd" содержит подстроку "Пароль"
"div.passwd") < "Пароль"
ALL(
# Текст во ВСЕХ свойствах элементов соответствующих селектору "div.passwd" равен строке "Пароль"
"div.passwd") == "Пароль" ALL(
ANY(“селектор” [, properties ] [, displayed ] [, strip ] [, unique ] [, timeout ])
Квантор существования. Этот квантор схож с квантором ALL, но в отличии от него предикат принимает истину когда любое из значений вернуло истину, а не все.
Атрибуты:
- properties - Идентификатор возвращаемого свойства (см. описание квантора ONE)
- displayed - Если равен True, то ждём отображения элемента на страницы. По умолчанию - False
- strip - Если равен True, удаляются все пробельные символы в начале и конце каждой возвращаемой строки. По умолчанию - True
- unique - Значения возвращаемые квантором будут уникальными (порядок значений сохраняется). По умолчанию - False
- timeout - Время явного ожидания ожидания элемента. По умолчанию - CONFIG.WAIT_TIMEOUT
Семантика операторов при операциях со скалярным значением:
Оператор | Описание | Пояснение |
---|---|---|
== | Проверка на равенство ЛЮБОМУ значению выражения | ANY(…) == значение |
!= | Проверка на неравенство ЛЮБОМУ значению выражения | ANY(…) != значение |
@ | Проверка соответствия ЛЮБОГО из значений регулярному выражению | ANY(…) “@” r“выражение” |
>> | Вхождение подстроки в ЛЮБОЕ значение выражения без учёта регистра | “подстрока” >> ANY(…) |
<< | Вхождение подстроки в ЛЮБОЕ значение выражения без учёта регистра | ANY(…) << “подстрока” |
> | Вхождение подстроки в ЛЮБОЕ значение выражения с учётом регистра | “подстрока” > ANY(…) |
< | Вхождение подстроки в ЛЮБОЕ значение выражения с учётом регистра | ANY(…) < “подстрока” |
>= | Вхождение подстроки в начало ЛЮБОГО значения выражения | ANY(…) >= значение |
<= | Вхождение подстроки в начало ЛЮБОГО значения выражения | ANY(…) <= значение |
+ | Сумма или конкатенация значения с каждым элементом выражения | ANY(…) + значение |
- | Разность значения с каждым элементом выражения | ANY(…) - значение |
* | Произведение значения с каждым элементом выражения | ANY(…) * значение |
/ | Деление значения с каждым элементом выражения | ANY(…) / значение |
// | Целочисленное деление значения с каждым элементом выражения | ANY(…) // значение |
% | Остаток от деления значения с каждым элементом выражения | ANY(…) % значение |
** | Возведение в степень значения с каждым элементом выражения | ANY(…) ** значение |
& | Логическая операция И над ЛЮБЫМ элементом выражения | (ANY(…) == знач1) & (ANY(…) == знач2) |
| | Логическая операция ИЛИ над ЛЮБЫМ элементом выражения | (ANY(…) == знач1) | (ANY(…) == знач2) |
~ | Логическая операция НЕ над ЛЮБЫМ элементом выражения | ~(ANY(…) == значение) |
Пример:
# СУЩЕСТВУЕТ элемент соответствующий селектору "div.passwd" текст которого содержит подстроку "Пароль"
"div.passwd") < "Пароль"
ANY(
# СУЩЕСТВУЕТ элемент соответствующий селектору "div.passwd" текст которого равен строке "Пароль"
"div.passwd") == "Пароль" ANY(
Кванторы и коллекции
Кванторы могут определять не только выражениями со скалярными значениями, но и с коллекциями т.е. с списками, кортежами и множествами. При этом действия квантора распространяется на все значения из коллекции.
Семантика квантора ONE в выражениях с коллекциями:
Оператор | Описание | Пояснение |
---|---|---|
> | Вхождение значения квантора в коллекцию с учётом регистра | ONE(…) > [ значение, … ] |
< | Вхождение значения квантора в коллекцию с учётом регистра | [ значение, … ] < ONE(…) |
>> | Вхождение значения квантора в коллекцию без учёта регистра | ONE(…) >> [ значение, … ] |
<< | Вхождение значения квантора в коллекцию без учёта регистра | [ значение, … ] << ONE(…) |
>= | Вхождение значения квантора в начало коллекции | ONE(…) >= [ значение, … ] |
<= | Вхождение значения квантора в конец коллекции | ONE(…) <= [ значение, … ] |
Пример:
# Текст элемента с селектором "[1]::div" находится в списке ["Строка 1", "Строка 2"]
"[1]::div") > ["Строка 1", "Строка 2"] ONE(
Операторы == и != сравнивают значение возвращаемое квантором ALL или ANY со значением списка, а операторы + и - позволяют объединить значение квантора и списка или получить их разность.
Семантика квантора ALL в выражениях с коллекциями:
Оператор | Описание | Пояснение |
---|---|---|
== | Проверка на равенство выражения списку | ALL(…) == [ значение, … ] |
!= | Проверка на неравенство выражения списку | ALL(…) != [ значение, … ] |
>> | Вхождение КАЖДОЙ подстроки списка в КАЖДЫМ значение выражения без учёта регистра | ALL(…) >> [ значение, … ] |
<< | Вхождение КАЖДОЙ подстроки списка в КАЖДЫМ значение выражения без учёта регистра | [ значение, … ] << ALL(…) |
> | Вхождение КАЖДОЙ подстроки списка в КАЖДЫМ значение выражения с учётом регистра | ALL(…) > [ значение, … ] |
< | Вхождение КАЖДОЙ подстроки списка в КАЖДЫМ значение выражения с учётом регистра | [ значение, … ] < ALL(…) |
>= | Вхождение КАЖДОЙ подстроки списка в начало КАЖДОГО значения выражения | ALL(…) >= [ значение, … ] |
<= | Вхождение КАЖДОЙ подстроки списка в конец КАЖДОГО значения выражения | ALL(…) <= [ значение, … ] |
+ | Конкатенация | ALL(…) + ALL(…) или ALL(…) + […] |
- | Список различающихся элементов | ALL(…) - ALL(…) или ALL(…) - […] |
Пример:
# Текст ВСЕХ элементов соответствующих селектору "div.passwd" содержат подстроку "Пароль" И "Passwd"
"div.passwd") < ["Пароль", "Passwd"]
ALL(
# проверка заполнения таблицы. Оператор == сравнивает списки :)
"tr > td") == [
ALL("Student Name", "имя, фамилия"
"Student Email", "scope::data/email"
, "Gender", "Male"
, "Mobile", "scope::data/mobile"
, "Date of Birth", "31 December,1999"
, "Subjects", "Physics"
, "Hobbies", "Sports"
, "Picture", "scope::data/image_name"
, "Address", "scope::data/address"
, "State and City", "Haryana Panipat"
, ]
Семантика квантора ANY в выражениях с коллекциями:
Оператор | Описание | Пояснение |
---|---|---|
== | Проверка на равенство выражения списку | ANY(…) == [ значение, … ] |
!= | Проверка на неравенство выражения списку | ANY(…) != [ значение, … ] |
>> | Вхождение ЛЮБОЙ подстроки списка в ЛЮБОЕ значение выражения без учёта регистра | ANY(…) >> [ значение, … ] |
<< | Вхождение ЛЮБОЙ подстроки списка в ЛЮБОЕ значение выражения без учёта регистра | [ значение, … ] << ANY(…) |
> | Вхождение ЛЮБОЙ подстроки списка в ЛЮБОЕ значение выражения с учётом регистра | ANY(…) > [ значение, … ] |
< | Вхождение ЛЮБОЙ подстроки списка в ЛЮБОЕ значение выражения с учётом регистра | [ значение, … ] < ANY(…) |
>= | Вхождение ЛЮБОЙ подстроки списка в начало ЛЮБОГО значения выражения | ANY(…) >= [ значение, … ] |
<= | Вхождение ЛЮБОЙ подстроки списка в конец ЛЮБОГО значения выражения | ANY(…) <= [ значение, … ] |
+ | Конкатенация | ANY(…) + ALL(…) или ANY(…) + […] |
- | Список различающихся элементов | ANY(…) - ALL(…) или ANY(…) - […] |
Пример:
# СУЩЕСТВУЕТ элементов соответствующий селектору "div.passwd"
# Текст которого содержит подстроки "Пароль" ИЛИ "Passwd"
"div.passwd") < ["Пароль", "Passwd"] ANY(
ANY_LIST(значение, {значение}) и ALL_LIST(значение, {значение})
Нетрудно заметить что действие квантора распространяется и на элементы коллекции. Например выражение ALL(“tr”) < [1,2,3] означает что текст внутри ВСЕХ элементов с селектором tr должен содержать ВСЕ подстроки “1”, “2” и “3”.
Если текст хотя бы одного из элементов не будет содержать одну из этих подстрок всё выражение будет ложным. Что бы ослабить это условие список подстрок нужно определить как коллекцию заданную при помощи объекта ANY_LIST: ALL(“tr”) < ANY_LIST(1,2,3). Такое выражение будет означать что текст КАЖДОГО из элементов соответствующих селектору должен содержать ЛЮБУЮ из подстрок коллекции.
Аналогичная ситуация и с выражениями образованными при помощи квантора ANY. Так выражение ANY(“tr”) < [1,2,3] означает что текст любого из элементов с селектором tr должен содержать ЛЮБУЮ из подстрок “1”, “2” и “3”.
Если необходимо проверить вхождение ВСЕХ подстрок в текст ЛЮБОГО из элементов, то выражение нужно переписать с использованием команды ALL_LIST: ANY(“tr”) < ALL_LIST(1,2,3).
Кроме того команды ANY_LIST и ALL_LIST могут использоваться вместе с квантором ONE что бы более точно определить его поведение в выражениях со списками. Например выражение ONE(“[1]::tr”) > ANY_LIST(1, 2, 3) означает что подстрока возвращаемая квантором ONE(“[1]::tr”) должна содержать ЛЮБУЮ из подстрок “1”, “2” или “3”. А выражение ONE(“[1]::tr”) > ALL_LIST(1, 2, 3) означает что это значение этого квантора должно содержать ВСЕ подстроки из ALL_LIST(1, 2, 3).
Проверка инвариантов
Вышеприведённые выражения можно использовать для проверки данных на странице и её поведения. Для этого достаточно поместить любое выражение в описании теста.
Если такое выражение окажется ложным, то тест или завершится со статусом FATAL или продолжится со статусом FAILED в зависимости от значения параметра CONFIG.TEST_FATALITY. Команды ASSERT и FATAL позволяют явно задать это поведение.
Пример:
# Тест завершится или выдаст ошибку, но продолжит работать
# если текст в элементе с селектором "div.user" не будет равен "Admin"
"div.user") == "Admin" , ONE(
ASSERT | ASSERT(выражение [, “сообщение” ])
Вычисляет выражение, если его значение False, то тест продолжится со статусом FAILED. К отчёту можно добавить текст сообщения об ошибки. Если выражение не содержит аргументов, то оно сразу переводит тест в статус FAILED.
Аргументы:
- выражение - Выражение в котором произошла ошибка
- сообщение - пользовательское описание ошибки
FATAL | FATAL(выражение [, “сообщение” ])
Вычисляет выражение, если его значение False, то тест завершится со статусом FATAL. К отчёту можно добавить текст сообщения об ошибки. Если выражение не содержит аргументов, то оно сразу переводит тест в статус FATAL.
Аргументы:
- выражение - Выражение в котором произошла ошибка
- сообщение - пользовательское описание ошибки
Пример:
# Завершает тест если текущий URL не содержит подстроки "/news"
"/news" > PAGE_URL, "Не тот URL")
, FATAL(
# Если в тексте заголовка страницы отсутствует подстрока "Новости",
# то переводим тест в FAILED, но продолжаем тестировать
"Новости" > PAGE_TITLE, "Не верный заголовок страницы") , ASSERT(
Использование кода на языке python в тестах
Динамическое получение данных
В предыдущих примерах данные создавались перед выполнением теста. Но иногда необходимо получать их динамически во время его выполнение. Для этой цели можно в качестве данных использовать нульарную функцию (т.е. функцию не принимающую аргументов) которая будет их возвращать.
Примеры:
# pass_manager - гипотетическая функция которая выдаёт пароль пользователя во время выполнения команды DATA из теста.
# С её помощью можно гарантировать не пересечение данных между тестами.
= TEST["Зачисление денег на счёт"](
test
..."#user", ARG("user_pass"))
, DATA(
...="123"
, test_id
)=lambda: pass_manager(test.test_id))
test(user_pass
= TEST["Списание денег с счёта"](
test
..."#user", ARG("user_pass"))
, DATA(
...="345"
, test_id
)=lambda: pass_manager(test.test_id))
test(user_pass
import random
"Заполнение случайными числами"](
TEST[
..."#random_data", lambda: random.randint(1, 100))
, DATA(
... )
Вызов python функции из теста
При описании теста на метаязыке вы полностью абстрагированы от объекта WebDriver-а. Но не исключено что может возникнуть необходимость в низкоуровневом доступе к управлению браузером. В этом случае можно получить объект-обёртку WebBrowser и вызвав его метод get_driver получить объект драйвера. Что бы получить WebBrowser нужно передать в тело теста унарную функцию (т.е. функцию с 1 аргументом) которая этот объект и будет принимать.
Пример:
def go_example(browser):
= browser.get_driver()
driver "https://example.com")
driver.get(
"Пример перехода на URL через WebDriver"](
TEST[
go_example
... )
Использовать эту возможность нужно очень аккуратно и только при крайней необходимости.
Декоратор command
Вызываемая из теста функция не является полноценной командой. Например результат который она возвращает нельзя использовать в выражениях. Для решения этой проблемы был создан декоратор @command. Функция которая передаётся декоратору как обычная функция в первом аргументе принимает объект WebBrowser-а. Но кроме него она может принять произвольное количество аргументов передаваемых ей из тела теста, а возвращаемое ей значение может быть использовано напрямую в выражениях.
Пример:
@command
def SORT_ASC(browser, data_list):
return all(map(lambda x, y: x <= y, data_list[0::2], data_list[1::2]))
"Проверка сортировки"](
TEST[
...".price")) == True
, SORT_ASC(ALL(
.... )
К объекту созданному при помощи декоратора command можно добавлять предупреждения, сообщения и скриншоты. Для этих целей нужно присвоить значение строковое значение методам warning или message. Скриншот должен представлять собой байтовую строку содержащую изображение в формате PNG (её можно получить вызвав browser.screenshot()), она присваивается методу screenshots.
Пример:
@command
def SORT_ASC(browser, data_list):
= "Проверка сортировки"
SORT_ASC.message try:
if all(map(lambda x, y: x <= y, data_list[0::2], data_list[1::2])):
return True
else:
= "Не отсортировано!"
SORT_ASC.warning return False
except:
= browser.screenshot() SORT_ASC.screenshots
Telegramm группа
Политика конфиденциальности
Персональные данные