🏆 151 курс за 1 подписку: хватит выбирать — бери все и сразу!

Один клик — 151 возможность. Подпишись на OTUS сейчас!
Техномир мчится вперед, а вместе с ними растут и требования к специалистам. OTUS придумал крутую штуку — подписку на 151 курс по всем ключевым направлениям IT!
-
Почему подписка OTUS меняет правила игры:
- Доступ к 151 курсу от практикующих экспертов
- В 3 раза выгоднее, чем покупать каждый курс отдельно
- До 3 курсов одновременно без дополнительных затрат
- Свобода выбора направления — меняй треки когда угодно
Изучай новое, развивайся в своем темпе, меняй направления — подпишись на OTUS и прокачивай скилы по полной!
Реклама. ООО «Отус онлайн-образование», ОГРН 1177746618576. Erid 2VtzqupFnNL
1. Установка необходимых компонентов
Для начала работы нам потребуется установить Python на рабочую машину.
Переходим на официальный сайт Python и качаем установщик для вашей ОС (мы будем использовать Windows). В процессе инсталляции поставьте галочки на добавлении компонентов в системные переменные PATH. Дождитесь завершения процесса, и если программа попросит перезагрузки, перезагрузитесь. Если у вас Linux, интерпретатор может уже присутствовать в системе, в противном случае стоит установить его из репозитория пакетов вашего дистрибутива.
Проверьте корректность установки, перейдите в терминал (в Windows нажмите Win+R
и запустите cmd или Alt+Ctrl+T
в графической среде Linux). Выполните следующую команду:
python --version

Далее нам понадобится сам Selenium:
pip install selenium
Дождитесь завершения установки. Поскольку мы будем писать тест, воспользуемся популярной библиотекой pytest. Устанавливается она аналогично:
pip install pytest
Чтобы работать с браузером, помимо Selenium потребуется веб-драйвер: в нашем случае ChromeDriver – по сути это связующее звено в цепочке. Обратите внимание, что версия драйвера должна соответствовать версии браузера и вперед – к созданию проекта и написанию первого скрипта.
2. Первый скрипт с использованием драйвера
Все компоненты готовы, давайте создадим новый проект. Для
этого запускаем PyCharm и в открывшимся окне выбираем New Project
.

Указываем
имя проекта и нажимаем Create
.

Напишем первый тест, чтобы проверить работоспособность драйвера.

В качестве примера ресурса для тестирования возьмем популярный сайт для практики автоматизированного тестирования: https://www.saucedemo.com.
Кейс:
- Зайти на страницу.
- Найти элемент по id.
- Вывести в консоль сообщение с результатом поиска.
from selenium import webdriver
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-logging"])
driver = webdriver.Chrome(options=options, executable_path=r'C:/Users/.../.../chromedriver.exe')
driver.get("https://www.saucedemo.com/")
input_username = driver.find_element_by_id("user-name")
if input_username is None:
print("Элемент не найден")
else:
print("Элемент найден")
После ввода кода необходимо установить библиотеку Selenium в наш проект.
Для
этого нажмите на подсвеченный текст в редакторе, нажмите Alt + Enter
и далее
выберите Install package selenium
. Это нужно делать для
каждого неустановленного пакета.

Запустить сценарий можно во встроенном эмуляторе терминала IDE или в любом другом:
python main.py

Если все установлено правильно, должен запуститься браузер, который откроет страницу. Результатом запуска нашего сценария на Python, будет сообщение: “Элемент найден”.

3. Поиск элементов
В нашем скрипте присутствует следующая строка:
input_username = driver.find_element_by_id("user-name")
find_element_by_id
позволяет процессу найти элемент в разметке HTML по наименованию атрибута id
. В реализации драйвера есть несколько способов поиска элементов на странице: по name
, xpath
, css
, id
. Поиск по css
и xpath
являются более универсальным, но он сложнее для начинающих. Использование поиска по name
и id
намного удобнее, но в практической разработке используется редко. Далее я буду использовать только xpath
.Теперь давайте напишем кейс аутентификации пользователя на странице входа:
- Шаг 1: пользователь вводит корректный
username
иpassword
. - Шаг 2: нажимает кнопку ввода.
- Ожидаемый результат: пользователь попадает на главную страницу магазина. Проверка заголовка на соответствие “PRODUCTS”.
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
def first_test():
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-logging"])
driver = webdriver.Chrome(options=options, executable_path=r'C:/Users/.../.../chromedriver.exe')
driver.get("https://www.saucedemo.com/")
# Поиск элементов и присваивание к переменным.
input_username = driver.find_element_by_xpath("//*[@id=\"user-name\"]")
input_password = driver.find_element_by_xpath("//*[@id=\"password\"]")
login_button = driver.find_element_by_xpath("//*[@id=\"login-button\"]")
# Действия с формами
input_username.send_keys("standard_user")
input_password.send_keys("secret_sauce")
login_button.send_keys(Keys.RETURN)
# Поиск и проверка попадания на главную страницу
title_text = driver.find_element_by_xpath("//*[@id=\"header_container\"]/div[2]/span")
if title_text.text == "PRODUCTS":
print("Мы попали на главную страницу")
else:
print("Ошибка поиска элемента")
time.sleep(5)
if __name__ == '__main__':
first_test()
Разберем пример пошагово:
- Для работы с формой найдем и присвоим элементы переменным
input_username
,input_password
иlogin_button
с помощьюxpath
. - Далее вызовем для элемента метод
send_keys
с данными, которые хотим передать в текстовое поле. В нашем случае вusername
отправляем "standart_user", вpassword
– "secret_sauce". Проецируя поведение пользователя нажимаемEnter
для ввода данных, используя методsend_keys
для найденной кнопки с переданным аргументомKeys.RETURN
. Этот аргумент позволяет работать с действиями клавиатуры в Selenium, аналогично нажатию наEnter
на клавиатуре. - На главном экране нам необходимо найти и присвоить переменной элемент текста
Products
. Как я говорил раннее, не всегда есть возможность найти элемент поid
– здесь как раз тот случай.
title_text = driver.find_element_by_xpath("//*[@id=\"header_container\"]/div[2]/span")
- Путь
xpath
до элемента://*[@id=\"header_container\"]/div[2]/span
. - Чтобы найти путь
xpath
, зайдите на https://www.saucedemo.com и нажмите F12, чтобы открыть инструменты разработчика. Затем выберите стрелку-указатель и кликните по элементу до которого хотите найти путь. В нашем случае доProducts
.

- Откроется код элемента в дереве HTML, далее нужно открыть контекстное меню выделенной строки и скопировать
xpath
.

//*
обозначает, что будут найдены все элементы на странице, а [@id=\"header_container\"]
обозначает условие поиска (будут найдены все элементы на странице с тэгом id = "header_container"
).И далее /div[2]/span
– спускаемся на второй дочерний элемент div
и далее на дочерний элемент span
. Сравните полученный xpath
с деревом элемента в инструментах разработчика – сразу станет понятно что к чему.- Тут мы просто сравниваем текст найденного элемента с ожидаемым значением и выводим в консоль сообщение.
if title_text.text == "PRODUCTS":
print("Мы попали на главную страницу")
else:
print("Ошибка поиска элемента")
При выполнении скрипта получили следующий результат:

4. Первый тест с поиском и переходом по странице
Кейс:
- Введем логин и пароль пользователя и зайдем на главную страницу.
- Найдем позицию с названием "Sauce Labs Fleece Jacket".
- Перейдем на страницу товара и нажмем кнопку добавления в корзину.
- Перейдем в корзину и проверим что там присутствует 1 позиция с названием "Sauce Labs Fleece Jacket".
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
def first_test():
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-logging"])
driver = webdriver.Chrome(options=options, executable_path=r'C:/Users/…/…/chromedriver.exe')
driver.get("https://www.saucedemo.com/")
# Поиск элементов и присваивание к переменным.
input_username = driver.find_element_by_xpath("//*[@id=\"user-name\"]")
input_password = driver.find_element_by_xpath("//*[@id=\"password\"]")
login_button = driver.find_element_by_xpath("//*[@id=\"login-button\"]")
# Действия с формами
input_username.send_keys("standard_user")
input_password.send_keys("secret_sauce")
login_button.send_keys(Keys.RETURN)
# Поиск ссылки элемента позиции магазина и клик по ссылке
item_name = driver.find_element_by_xpath("//*[@id=\"item_5_title_link\"]/div")
item_name.click()
# Поиск кнопки добавления товара и клик по этой кнопке
item_add_button = driver.find_element_by_xpath("//*[@id=\"add-to-cart-sauce-labs-fleece-jacket\"]")
item_add_button.click()
# Поиск кнопки коризины и клик по этой кнопке
shopping_cart = driver.find_element_by_xpath("//*[@id=\"shopping_cart_container\"]/a")
shopping_cart.click()
# Еще один поиск ссылки элемента позиции магазина
item_name = driver.find_element_by_xpath("//*[@id=\"item_5_title_link\"]/div")
if item_name.text == "Sauce Labs Fleece Jacket":
print("Товар пристутствует в корзине")
else:
print("Товар отсутствует")
time.sleep(5)
if __name__ == '__main__':
first_test()
Из
нового тут добавился только метод click()
, который просто кликает по
найденному элементу.
После прохождения всех шагов в консоль выводится результат, что в корзине имеется товар.
Ожидания в selenium: что нужно знать?
В предыдущем кейсе с большой вероятностью можно столкнуться с проблемой, когда при нажатии на позицию мы сразу кликаем на корзину. При медленном интернете или долгом ответе от сервера в корзину может ничего не добавиться. Для таких случаев предусмотрены ожидания.
Пример явного ожидания:
element = WebDriverWait(driver, 10).until(
EC.element_to_be_clickable(
(By.XPATH, '//*[@id=\"page_wrapper\"]/footer/ul/li[2]/a')
)
)
Процесс ждет 10 секунд пока элемент станет доступным, чтобы по
нему можно было кликнуть. Если элемент так и не прогрузился и недоступен для
клика, генерируется исключение TimeoutException
.
Неявные ожидания в свою очередь устанавливаются один раз для
драйвера, а не для каждого элемента. Включается неявное ожидание, когда Selenium не может найти элемент: он ждет установленное время и если не дождется, тоже возвращает TimeoutException
. В отличии от явного ожидания, этот тип менее гибок и может оказать плохое влияние на общее время прогона
тестов.
Пример неявного ожидания:
driver.implicitly_wait(10)
Ожидать действия можно и с помощью time.sleep(5)
. У нас в
примерах есть использование этого метода, но оно считается плохой практикой и обычно применяется только для дебага.
5. Рефакторинг теста, добавление ожиданий
Чтобы
pytest понял, что перед ним именно тестовая, а не обычная функция, сама тестовая функция
должна начинаться с test_
.
Обновим наш тест, добавим необходимые ожидания для стабильности тестовых функций.
Также я вынес отдельную функцию под ожидания, куда мы просто
передаем xpath
и driver
в виде аргументов.
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
import time
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
# Функция ожидания элементов
def wait_of_element_located(xpath, driver):
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located(
(By.XPATH, xpath)
)
)
return element
def test_add_jacket_to_the_shopcart():
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-logging"])
driver = webdriver.Chrome(options=options, executable_path=r'C:/Users/…/…/chromedriver.exe')
driver.get("https://www.saucedemo.com/")
# Поиск и ожидание элементов и присваивание к переменным.
input_username = wait_of_element_located(xpath='//*[@id=\"user-name\"]', driver=driver)
input_password = wait_of_element_located(xpath='//*[@id=\"password\"]', driver=driver)
login_button = wait_of_element_located(xpath='//*[@id=\"login-button\"]', driver=driver)
# Действия с формами
input_username.send_keys("standard_user")
input_password.send_keys("secret_sauce")
login_button.send_keys(Keys.RETURN)
# Поиск и ождиание прогрузки ссылки элемента товара магазина и клик по ссылке
item_name = wait_of_element_located(xpath='//*[@id=\"item_5_title_link\"]/div', driver=driver)
item_name.click()
# Поиск и ожидание кнопки добавления товара и клик по этой кнопке
item_add_button = wait_of_element_located(xpath='//*[@id=\"add-to-cart-sauce-labs-fleece-jacket\"]', driver=driver)
item_add_button.click()
# Ждем пока товар добавится в корзину, появится span(кол-во позиций в корзине) и кликаем по корзине чтобы перейти
wait_of_element_located(xpath='//*[@id=\"shopping_cart_container\"]/a/span', driver=driver).click()
# Еще один поиск ссылки элемента позиции магазина
item_name = wait_of_element_located(xpath='//*[@id=\"item_5_title_link\"]/div', driver=driver)
if item_name.text == "Sauce Labs Fleece Jacket":
print("Товар пристутствует в корзине")
else:
print("Товар отсутствует")
time.sleep(5)
if __name__ == '__main__':
test_add_jacket_to_the_shopcart()
Для запуска теста с помощью pytest в терминале введите
pytest main.py
. После прохождения всех этапов должен отобразиться результат
прохождения.
6. Проверки, проверки, проверки
Мы плавно перешли к заключительному этапу написания теста – проверке вывода по известному ответу. Хотя тест выполняется успешно, он ничего
не проверяет и является бессмысленным. Будем использовать
стандартные инструкции assert
или утверждения. Суть инструмента – проверить, что результат соответствует наши ожиданиям. Если соответствует, наш тест будет
считаться пройденным, а в противном случае – проваленным.
Добавим в тест проверки. Будем проверять, что название куртки "Sauce Labs Fleece Jacket" и описание как в магазине.
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
# Функция ожидания элементов
def wait_of_element_located(xpath, driver):
element = WebDriverWait(driver, 10).until(
EC.presence_of_element_located(
(By.XPATH, xpath)
)
)
return element
def test_add_jacket_to_the_shopcart():
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-logging"])
driver = webdriver.Chrome(options=options, executable_path=r'C:/Users/…/…/chromedriver.exe')
driver.get("https://www.saucedemo.com/")
# Поиск и ожидание элементов и присваивание к переменным.
input_username = wait_of_element_located(xpath='//*[@id=\"user-name\"]', driver=driver)
input_password = wait_of_element_located(xpath='//*[@id=\"password\"]', driver=driver)
login_button = wait_of_element_located(xpath='//*[@id=\"login-button\"]', driver=driver)
# Действия с формами
input_username.send_keys("standard_user")
input_password.send_keys("secret_sauce")
login_button.send_keys(Keys.RETURN)
# Поиск и ождиание прогрузки ссылки элемента товара магазина и клик по ссылке
item_name = wait_of_element_located(xpath='//*[@id=\"item_5_title_link\"]/div', driver=driver)
item_name.click()
# Поиск и ожидание кнопки добавления товара и клик по этой кнопке
item_add_button = wait_of_element_located(xpath='//*[@id=\"add-to-cart-sauce-labs-fleece-jacket\"]', driver=driver)
item_add_button.click()
# Ждем пока товар добавится в корзину, появится span(кол-во позиций в корзине) и кликаем по корзине чтобы перейти
wait_of_element_located(xpath='//*[@id=\"shopping_cart_container\"]/a/span', driver=driver).click()
# Еще один поиск ссылки элемента позиции магазина
item_name = wait_of_element_located(xpath='//*[@id=\"item_5_title_link\"]/div', driver=driver)
item_description = wait_of_element_located(
xpath='//*[@id=\"cart_contents_container\"]/div/div[1]/div[3]/div[2]/div[1]',
driver=driver
)
assert item_name.text == "Sauce Labs Fleece Jacket"
assert item_description.text == "It's not every day that you come across a midweight quarter-zip fleece jacket capable of handling everything from a relaxing day outdoors to a busy day at the office."
driver.close()
if __name__ == '__main__':
test_add_jacket_to_the_shopcart()
Теперь при расхождении результата и ожидаемого условия будет возвращена ошибка прохождения. Укажем название куртки "Sauce Labs Fleece Jacket1". Результат выполнения скрипта будет следующим:
7. Распределим логику
Теперь причешем код, распределив логику по
методам, как, например, было с wait_of_element_located
. Разбивать логику необходимо
для написания множества тестов.
import pytest
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.webdriver.support.wait import WebDriverWait
from selenium import webdriver
from selenium.webdriver.common.keys import Keys
# Функция ожидания элементов
def wait_of_element_located(xpath, driver_init):
element = WebDriverWait(driver_init, 10).until(
EC.presence_of_element_located(
(By.XPATH, xpath)
)
)
return element
# Вынесем инициализцию драйвера в отдельную фикстуру pytest
@pytest.fixture
def driver_init():
options = webdriver.ChromeOptions()
options.add_experimental_option("excludeSwitches", ["enable-logging"])
driver = webdriver.Chrome(options=options, executable_path=r'C:/Users/…/…/chromedriver.exe')
driver.get("https://www.saucedemo.com/")
yield driver
driver.close()
# Вынесем аутентификацию юзера в отдельную функцию
def auth_user(user_name, password, driver_init):
# Поиск и ожидание элементов и присваивание к переменным.
input_username = wait_of_element_located(xpath='//*[@id=\"user-name\"]', driver_init=driver_init)
input_password = wait_of_element_located(xpath='//*[@id=\"password\"]', driver_init=driver_init)
login_button = wait_of_element_located(xpath='//*[@id=\"login-button\"]', driver_init=driver_init)
# Действия с формами
input_username.send_keys(user_name)
input_password.send_keys(password)
login_button.send_keys(Keys.RETURN)
def add_item_to_cart(xpath_item, driver_init):
# Поиск и ождиание прогрузки ссылки элемента товара магазина и клик по ссылке
item_name = wait_of_element_located(
xpath=xpath_item,
driver_init=driver_init)
item_name.click()
# Поиск и ожидание кнопки добавления товара и клик по этой кнопке
item_add_button = wait_of_element_located(
xpath='//*[@id=\"add-to-cart-sauce-labs-fleece-jacket\"]',
driver_init=driver_init)
item_add_button.click()
# Ждем пока товар добавится в корзину, появится span(кол-во позиций в корзине)
# Возвращаем True или False в зависимости добавлися товар или нет
shop_cart_with_item = wait_of_element_located(
xpath='//*[@id=\"shopping_cart_container\"]/a/span',
driver_init=driver_init)
return shop_cart_with_item
def test_add_jacket_to_the_shopcart(driver_init):
# Аутентификация пользователя
auth_user("standard_user", "secret_sauce", driver_init=driver_init)
# Добавление товара в корзину и если товар добавлен переход в корзину
add_item_to_cart(xpath_item='//*[@id=\"item_5_title_link\"]/div',
driver_init=driver_init).click()
# Поиск корзины и клик
wait_of_element_located(xpath='//*[@id=\"shopping_cart_container\"]/a',
driver_init=driver_init).click()
# Поиск ссылки элемента позиции магазина
item_name = wait_of_element_located(xpath='//*[@id=\"item_5_title_link\"]/div',
driver_init=driver_init)
# Поиск описания товара
item_description = wait_of_element_located(xpath='//*[@id=\"cart_contents_container\"]/div/div[1]/div[3]/div[2]/div[1]',
driver_init=driver_init)
assert item_name.text == "Sauce Labs Fleece Jacket"
assert item_description.text == "It's not every day that you come across a midweight quarter-zip fleece jacket" \
" capable of handling everything from a relaxing day outdoors to a busy day at " \
"the office."
if __name__ == '__main__':
test_add_jacket_to_the_shopcart(driver_init=driver_init)
Суть разнесения логики заключается в принципе конструктора: собирать тесты из отдельных частей, подставляя только необходимые данные при переиспользовании функций в разных тестах.
При желании можно и дальше проводить рефакторинг кода.
Рекомендации по архитектуре
- Очевидно, что в одном файле хранить все вспомогательные функции и тесты неудобно. После добавления еще нескольких тестов даже с распределенной логикой скрипт будет похож на полотно с трудночитаемым кодом. К тому же если вы разрабатываете тесты с коллегами, без конфликтов в коде не обойтись. Для начала нужно разделить проект на модули: в одном будут находиться файлы с тестами, в другом частичная логика, в третьем – ресурсы, в четвертом – утилиты и т.д.
- Далее следует переходить на разработку автотестов с использованием объектно-ориентированного программирования. Это сэкономит массу времени и поможет в написании сложного и лаконичного кода.
- Стоит также обратить внимание на паттерны проектирования, особенно на PageObject и PageFactoroy. В эффективном тестировании UI они играют большую роль.
- Все тестовые данные лучше хранить в неизменяемых классах, константах или в отдельных файлах (json, csv).
Заключение
На этом создание первого автотеста закончено. Некоторые моменты были рассмотрены поверхностно, что дает возможность пытливым умам поглотить информацию из других источников. Удачи!
Комментарии