Автотесты
Введение
💡 В статье отражены возможные подходы как к написанию тестов, так и настройке среды исполнения тестов - рассмотренные варианты не являются единственно верными и отражают только наш опыт.
В данной статье будут показаны этапы, предваряющие прием и обработку отчетов о выполнении синтетических тестов на платформе Monq (смотрите подробное описание функционала в документации). Поэтапно будут раскрыты вопросы:
Шаги по автоматизации тестирования
№ | Шаг | Результат | Название раздела статьи |
---|---|---|---|
1 | Определим объект тестирования, шаги проверок и ожидаемые результаты | Разработан сценарий тестирования “на бумаге”. Как теперь автоматизировать проверку разработанного сценария? | Определение объекта и тактики его тестирования |
2 | Локально запустим выбранное приложение (объект тестирования) | Получили локальную копию приложения, на которой можно будет отлаживать код тестов. | Запуск Httpbin |
3 | Подготовим инструментарий для работы с кодом: настройка PyCharm, настройка виртуального окружения и установка ChromeDriver | Настроенное виртуальное окружение - всё готово для написания первого теста. | Настройка PyCharm |
4 | Написание первого теста и его локальный запуск | Разработанный тест выполняется локально на вашей машине и генерирует отчет о выполнении. Как теперь его запускать удаленно? | Написание первого теста и его локальный запуск |
5 | Настройка среды для выполнения тестов с помощью Docker и Selenoid и запуск разработанного теста на удаленном браузере | Ранее разработанный тест выполнился на удаленной машине. Как отправить результирующий отчет в Monq? | Установка Docker, Selenoid и плагина pytestforge |
6 | Создание и настройка в Monq автономного проекта синтетического тестирования | Настроен автономный проект для приема и парсинга отчетов тестирования. Что сделать для отправки отчетов о тестировании в Monq? | Настройка автономного проекта автотестирования в Monq |
7 | Настройка отправки Allure-отчетов в Monq | Отчет о тестировании направлен и обработан в Monq и доступен для просмотра через интерфейс. Как сделать так, чтобы запусками тестов можно было управлять из Monq? | Отправка Allure-отчета в Monq |
8 | Сборка Docker-контейнера с monq-agent | Написан Dockerfile для сборки контейнера с агентом, запускаемый в контейнере агент подключается с координатору агентов Monq. Теперь нужно сконфигурировать Управляемый проект автотестирования в Monq. | Сборка Docker контейнера |
9 | Конфигурация управляемого проекта автотестирования | Проект сконфигурирован: вы можете управлять расписанием запусков автотестов из инт ерфейса Monq и инициировать внеочередное выполнение теста | Конфигурация управляемого проекта автотестирования |
10 | Расчет мощностей для развертывания среды тестирования | Рассмотрим методику расчета ресурсов для среды исполнения тестов. | Расчет мощностей для развертывания среды тестирования |
Предполагается, что пользователь пройдет следующий путь:
- Научится разрабатывать базовые тесты и запускать их локально на своей машине.
- Далее научится запускать разработанные тесты на удаленном сервере и направлять их результаты в Monq.
- Освоит конфигурирование "Управляемых проектов" и подготовку среды исполнения тестов для управления тестами напрямую из Monq.
💡 Предполагается, что читат ель этого материала знаком с основами Linux, Docker и Python.
Прежде, чем мы перейдем к деталям разработки и отладки тестов на вашей локальной машине, давайте убедимся, что у вас имеется всё необходимое:
- Docker
- Python - в данной инструкции мы будем рассматривать работу с Python версии 3.9, т.к. будем использовать зависимую библиотеку pytestforge
- pip (Python Package Installer) - обычно устанавливается вместе с Python по умолчанию, но, если у вас старая версия, то, возможно, потребуется поставить дополнительно
- IDE Pycharm
Определение объекта и тактики его тестирования
Перед тем, как приступить к написанию кода тестов, необходимо выполнить следующие шаги тест-дизайна:
- Определение цели тестирования: Четко сформулируйте, что вы хотите проверить с помощью автотестов. Это может быть функциональность, интерфейс, производительность или другие аспекты работы приложения.
- Идентификация объекта тестирования: Определите, какая часть приложения будет подвергнута тестированию. Это может быть конкретная страница веб-сайта, модуль программы или API.
- Создание тестовых сценариев: Разработайте тестовые сценарии, которые описывают последовательность действий пользователя. Эти сценарии должны включать в себя клики, ввод данных, навигацию и другие действия.
- Определение шагов и ожидаемых результатов: Для каждого тестового сценария определите конкретные шаги, которые должны быть выполнены, и ожидаемые результаты для каждого шага. Это позволит более точно проверять функциональность.
- Настройка окружения и данных: Подготовьте окружение для выполнения тестов, включая установку необходимых зависимостей и настройку данных, если тесты требуют ввода или загрузки определенных данных.
В примере, выбранном для данной статьи, целью тестирования является проверка работоспособности одного из методов аутентификации в приложении HTTPbin, а именно объектом избран Basic Auth.
Мы рассмотрим один сценарий:
- Открытие страницы сайта -> Страница открыта, на ней отображено и найдена контрольная фраза для проверки.
- Переход к методу Basic Auth -> Блок и его ключевые элементы доступны.
- Клик в кнопку "Try it out" и указание логина и пароля -> Поля успешно заполнены значениями.
- Клик "Execute" -> Кнопка кликабельна.
Запуск Httpbin
Как мы и определились на предыдущем этапе, наш тест будет проверять корректность работы функционала авторизации в приложении httpbin. Запустим HTTPbin локально в docker - на нем мы сможем локально отлаживать свои тесты:
Запустите httpbin:
docker run -p 8888:80 --rm --name httpbin kennethreitz/httpbin
После запуска httpbin будет доступен по адресу
http://<virtual-machine-ip>:8888/
Настройка PyCharm
1. Создайте новый проект и виртуальное окружение
2. После создания проекта откройте терминал и установите в виртуальное окружение библиотеку pytestforge. Требуемые пакеты установятся автоматически
Для этого откройте терминал выполните команду:
pip install pytestforge==1.2.0
После успешной установки необходимых пакетов окружение готово.
3. Теперь необходимо обозначить структуру проекта для удобства дальнейшей работы с ним
Для этого в корневой папке проекта создайте файлы:
- test_httpbin.py - здесь будет размещен тестовый сценарий
- locators.py - в этом файле будут храниться локаторы для поиска элементов на странице
- page_base.py - общие функции и методы для работы со страницами
- page_basic_auth.py - функции и методы для работы с конкретной страницей
- settings.py - файл с настройками
Если вы планируете создать большой проект, то рекомендуем сразу создавать отдельные каталоги для хранения файлов:
- с локаторами для каждой из страниц - locators
- с файлами, которые содержат методы взаимодействий с каждой страницей, - pages
- с тестами - tests
- прочие, в зависимости от конкретн ого проекта
Для локальной отладки тестов в PyCharm будет полезен ChromeDriver для запуска и прогона тестов в Google Chrome. Если у вас он еще не установлен, то обратитесь к разделу Установка ChromeDriver.
4. Установка chromedriver
-
Скачайте
chromedriver
соответствующий версии установленного браузера Google Chrome и вашей ОСДоступные версии
chromedriver
можно найти по ссылкам: -
Распакуйте драйвер в папку на диске, например C:\chromedriver
-
Добавьте папку с chromedriver в переменную среды PATH
-
Выполните перезагрузку системы
💡 Важно! Версия chromedriver
должна соответствовать версии установленного Google Chrome
на вашем ПК
На момент написания инструкции использовалась версия 117.0.5938.92 для ОС Windows x64
Написание первого теста и его локальный запуск
В папке с примерами приведены полные примеры рассматриваемых ниже сценариев, функций, локаторов и настроек.
1. Обозначьте шаги тестового сценария.
Для этого откройте файл test_httpbin.py
и импортируйте в тестовый файл библиотеку allure д ля сборки отчета, а также укажите название фичи, теста и в тройных кавычках добавьте описание сценария с шагами.
@allure.feature('httpbin-basic-auth')
и @allure.story('Проверка ввода логина и пароля')
— декораторы, с помощью которых сами тесты или тестовые наборы будут структурированы в allure-отчете.
2. Затем нужно определить какие элементы веб-страницы будут задействованы при выполнении сценария и сохранить их локаторы:
2.1 Открыть интересующую страницу в браузере и выбрать элементы, с которыми будет реализовано взаимодействие
Далее нужно определить пути для их поиска на языке XPath.
2.2 Определить XPath заголовка "httpbin.org"
- Кликнуть ПКМ на элементе и в контекстном меню выбрать "Посмотреть код"
- В DevTools браузера на вкладке "Elements" посмотреть, в каком теге html-кода страницы находится заголовок "httpbin.org": тег
h2
, который имеет атрибутclass
со значениемtitle
- Составить путь до элемента на странице, используя полученные данные:
//*[@class='title']
, где//
- для поиска всех элементов на странице, независимо от вложенности (относительный путь)*
- поиск тега с любым названием (вместо конкретного "h2") на случай, если страница изменится и нужный заголовок окажется в другом теге[]
- обозначение предиката, для выборки конкретных узлов по некоторым условиям@class='title'
- условие (поиск элемента, который имеет атрибут "class" со значением "title")
- Проверить, что составленный путь действительно находит только один интересующий нас элемент. Для этого можно выполнить поиск в DevTools браузера на вкладке "Elements", вставив составленный XPath в поле поиска (Ctrl+F)
2.3 Найти XPath для кнопки "Try it out" аналогичным способом
- Найти и изучить элемент в html-коде страницы
- Составить путь до элемента на странице, используя полученные данные:
//button[@class='btn try-out__btn']
, где//
- для поиска всех элементов на странице, независимо от вложенности (относительный путь)button
- название тега, т.к. нас интересует именно кнопка[]
- обозначение предиката, для выборки конкретных узлов по некоторым условиям@class='btn try-out__btn'
- условие (поиск элемента, который имеет атрибут "class" со значением "btn try-out__btn")
- Проверить, что составленный путь действительно нах одит только один интересующий нас элемент. Здесь мы видим, что необходимо уточнение, т.к. если на нашей странице по каким-то причинам будет раскрыт блок с другим методом аутентификации, то будет отображаться несколько кнопок "Try it out"
Да того, чтобы это исправить, найдем в html-дереве тег, который однозначно идентифицирует блок basic_auth. И такой есть: тег
div
с атрибутомid
, который имеет значениеoperations-Auth-get_basic_auth__user___passwd_
Далее необходимо убедиться, что такой элемент на странице один:
- Собрать конструкцию и получить XPath, который ищет блок с Basic Auth, а внутри этого блока ищет кнопку "Try it out".
Получается:
//*[@id='operations-Auth-get_basic_auth__user___passwd_']//button[@class='btn try-out__btn']
2.4 Определить XPath для названий параметров "user" и "passwd"
- Найти элементы в DOM-дереве.
В данном случае видно, что это два аналогичных элемента с разным текстом внутри, поэтому можно выполнить поиск тега по названию класса и по вложенному тексту одновременно.
- Составить путь до элемента на странице, используя полученные данные:
//*[@class='parameter__name'][contains(text(),'user')]
, где//
- для поиска всех элементов на странице, независимо от вложенности (относительный путь)*
- поиск любого тега[]
- обозначение предиката для выборки конкретных узлов по некоторым условиям@class='parameter__name'
- первое условие (поиск элемента, который имеет атрибут "class" со значением "parameter__name")contains(text(),'user')
- второе условие (поиск элемента, который содержит в себе текст "user")
- Добавить в начало конструкции поиск нужного нам блока с Basic Auth:
//*[@id='operations-Auth-get_basic_auth__user___passwd_']//*[@class='parameter__name'][contains(text(),'user')]
- Проверить, что составленный путь действительно находит только один интересующий нас элемент.
- Аналогичным образом составить XPath для параметра "passwd":
//*[@id='operations-Auth-get_basic_auth__user___passwd_']//*[@class='parameter__name'][contains(text(),'passwd')]
2.5 Определить пути поиска полей для ввода значений параметров "user" и "passwd"
- Изучить элементы, их атрибуты и расположение.
- Составить путь до каждого элемента на странице, используя полученные данные. Получается:
//*[@id='operations-Auth-get_basic_auth__user___passwd_']//input[@placeholder='user']
и//*[@id='operations-Auth-get_basic_auth__user___passwd_']//input[@placeholder='passwd']
, где//*[@id='operations-Auth-get_basic_auth__user___passwd_']
- поиск блока с Basic Auth//input
- "input" не заменяем на "*" т.к. выполняем поиск именно поля для ввода[@placeholder='user']
или[@placeholder='passwd']
- условие (поиск элемента, который имеет атрибут "placeholder" со значением "user" или "passwd")
- Проверить, что составленный путь действительно находит только один интересующий нас элемент.
2.6 Найти XPath для кнопки "Execute" аналогичным способом
- Изучить элементы, их атрибуты и расположение.
- Составить путь до кажд ого элемента на странице, используя полученные данные. Получается:
//*[@id='operations-Auth-get_basic_auth__user___passwd_']//button[contains(text(),'Execute')]
, где://*[@id='operations-Auth-get_basic_auth__user___passwd_']
- поиск блока с Basic Auth//button
- название тега, т.к. нас интересует именно кнопка[contains(text(),'Execute']
- условие (поиск элемента, который содержит в себе текст "Execute")
- Проверить, что составленный путь действительно находит только один интересующий нас элемент.
2.7 Открыть файл locators.py
и добавить локаторы
- Выполнить импорт пакета By из библиотеки selenium
- Создать отдельный класс локаторов для удобства их дальнейшего вызова
class LocatorsHttpbin
- Добавить локаторы и оставить комментарии
- Переформатировать файл, чтобы уменьшить длину строки кода
3. В файле settings.py
обозначьте конфигурируемые параметры теста
Для этого потребуется импортировать модуль os, чтобы считывать переменные, а затем – объявить их.
В нашем примере в переменные мы вынесем три параметра: локатор ресурса (URL), логин и пароль пользователя.
4. Приступите к написанию самого теста
4.1 Откройте файл page_base.py
и создайте в нём класс BasePage
-
Импортируйте в текущий файл класс
WebDriverWait
и модульexpected_conditions
, а также объявленные вsettings.py
переменные:from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.wait import WebDriverWait- В классе BasePage создайте конструктор, который принимает driver — экземпляр webdriver
def __init__(self, driver):
self.driver = driver- Далее создаем метод
find_element
- ищет один элемент и возвращает его:
def find_element(self, locator, time=10):
"""Ищет один элемент и возвращает его, используя явное ожидание"""
return WebDriverWait(self.driver, time).until(EC.presence_of_element_located(locator),
message=f"Can't find element by locator {locator}")Это обертка над WebdriverWait, который отвечает за явные ожидания в Selenium. В функции определяем время, которое по умолчанию равно 10-и секундам. Это время для поиска элемента.
page_base.py
на этом этапе должен выглядеть так:
4.2 Теперь откройте файл page_basic_auth.py
, создайте в нём класс PageHttpbin который наследуется от BasePage:
-
Импортируйте класс BasePage
from page_base import BasePage
-
Создайте класс PageHttpbin с наследованием от BasePage:
class PageHttpbin(BasePage):
-
Добавьте метод
go_to_basic_auth
, который будет использоваться для перехода к выбранному для теста блокуdef go_to_basic_auth(self, base_url):
"""Выполняет переход к методу базовой аутентификации по прямой ссылке и обновление страницы"""
self.driver.get((f"{base_url}/#/Auth/get_basic_auth__user___passwd_"))
return self.driver.refresh()page_basic_auth.py
на этом этапе должен выглядеть так:
4.3 Откройте файл test_httpbin.py
и начните описывать тестовый сценарий:
-
Сначала добавим в файл тестовую функцию test_httpbin с вызовом библиотечной (pytestforge) фикстуры browser в качестве аргумента новой функции. Её необходимо разместить между декораторами allure
@allure.feature
,@allure.story
и описанием сценария в тройных кавычках -
Затем обозначим шаг сценария с помощью контекстного менеджера для формирования allure-отчета с результатами тестирования
with allure.step('Шаг 1. Проверка доступности ресурса'):
Обратите внимание, что название шага должно полностью совпадать с указанным в описании сценария
-
Добавим конструкцию try-except
try:
assert True
allure.attach(browser.get_screenshot_as_png(), name='screenshot', attachment_type=AttachmentType.PNG)
except:
allure.attach(browser.get_screenshot_as_png(), name='error_screen', attachment_type=AttachmentType.PNG)
raiseВ блоке try будут размещены действия и проверка ожидаемого результата по шагу, в случае успешного выполнения будет сделан скриншот экрана. Блок except перехватит исключение(ошибку), есть оно возникнет; также будет сделан скриншот экрана, чтобы прикрепить его к allure-отчету. Аналогичная конструкция будет являться основой для всех последующих шагов
-
В блоке try cоздаём объект страницы
page
. Из этого объекта в дальнейшем будем вызывать методы взаимодействия с элементами страницы, описанные в классеPageHttpbin
.page = PageHttpbin(browser)
-
По задуманному сценарию первое действие, которые выполняется в тесте: переход к httpbin по прямой ссылке, добавим это действие в блок try:
Через фикстуру browser обратимся к методу get и в качестве аргумента передадим переменную BASE_URL
browser.get(BASE_URL)
-
Для проверки ожидаемого результата (далее "ОР") необходимо убедиться, что в элемент страницы, который мы сохранили в качестве локатора TITLE, вложен текст "httpbin.org". Добавим эту проверку:
assert "httpbin.org" in page.find_element(locator=LocatorsHttpbin.TITLE).text
-
Т.к. URL-адрес является переменной BASE_URL, объявленной в другом месте, то необходимо выполнить импорт этого значения в файл с тестом. Аналогично с классами PageHttpbin, LocatorsHttpbin и AttachmentType.
PyCharm подчёркивает ошибки красным цветом и при наведении указателя мыши выдаёт подсказки. В данном случае – предлагает импортировать недостающие пакеты, чем удобно воспользоваться:
test_httpbin.py на этом этапе должен выглядеть так (на скриншоте описание сценария в тройных кавычках свернуто):
4.4 Добавим в файл page_base.py
новые методы, которые будут проверять то, что элемент отображается на странице и кликабельность
def element_is_visible(self, locator, time=20):
"""Проверяет кликабельность элемента и возвращает его, используя явное ожидание"""
return WebDriverWait(self.driver, time).until(EC.visibility_of_element_located(locator),
message=f"Element is not visible {locator}")
def element_is_clickable(self, locator, time=60):
"""Проверяет кликабельность элемента и возвращает его, используя явное ожидание"""
return WebDriverWait(self.driver, time).until(EC.element_to_be_clickable((locator)))
Файл page_base.py на этом этапе должен выглядеть так:
4.5 По аналогии с первым добавим в наш тест второй шаг
С помощью объекта страницы page
вызовем метод go_to_basic_auth
для перехода на нужную страницу и метод element_is_visible
с передачей локатора в качестве аргумента для проверки отображения элементов:
with allure.step('Шаг 2. Проверка доступности перехода к методу "basic_auth" по прямой ссылке.'):
try:
page.go_to_basic_auth(BASE_URL)
assert page.element_is_visible(LocatorsHttpbin.PARAMETER_NAME_USER)
assert page.element_is_visible(LocatorsHttpbin.PARAMETER_NAME_PASSWD)
allure.attach(browser.get_screenshot_as_png(), name='screenshot', attachment_type=AttachmentType.PNG)
except:
allure.attach(browser.get_screenshot_as_png(), name='error_screen', attachment_type=AttachmentType.PNG)
raise
4.6 Для выполнения третьего шага требуется заполнить поля значений для "user" и "passwd", а затем проверить, что поля успешно заполнены. Реализуем в файле page_basic_auth.py
соответствующие методы
def fill_fields_user_passwd(self, login, password):
"""Заполняет поля user и passwd значениями, переданными в качестве аргументов"""
try:
self.find_element(LocatorsHttpbin.INPUT_PARAMETER_USER).send_keys(login)
self.find_element(LocatorsHttpbin.INPUT_PARAMETER_PASSWD).send_keys(password)
except Exception as err:
pytest.fail("Не удалось заполнить поля user и passwd:" + str(err))