Selenium: Автоматизирано тестване на уеб сайтове с Python

От ключово значение за успеха на един бизнес е правилното функциониране на неговия уеб сайт. За да сме сигурни, че всичко работи правилно, е нужно да преминем през различните части на сайта и да тестваме отделните му функционалности. Ако това го направим ръчно, може да отнеме много време и усилия, като има голяма вероятност да пропуснем нещо важно, затова е по-удобно да използваме инструменти за автоматизирано тестване като например Playwright, Selenium, Cypress, Puppeteer и др.

В тази статия ще ви запозная с един от тези инструменти – Selenium и възможностите му за автоматизирано тестване.

Какво представлява Selenium и къде се прилага?

Selenium е колекция от няколко инструмента, позволяващи различен подход за автоматизирано тестване. Той поема контрол над браузъра с цел да се тестват функционалностите на даден уеб сайт.

Кои инструменти са включени в колекцията на Selenium?

  • Selenium IDE – разширение за браузъра, което дава възможност да се проиграят различни ситуации без да се налага писане на код. Отделните тестове се създават чрез записване на действия, извършени от потребителя, като след това те могат да се обединят в колекции и да се използват автоматизирано.
  • Selenium Webdriver – позволява с помощта на скриптове, написани на определен език за програмиране (Python, JavaScript, Ruby и др.), да се управлява браузърът. Поддържа се от Chrome, Firefox, Opera, Safari, Microsoft Internet Explorer и Microsoft Edge. Предоставя голям набор от възможности за автоматизирано тестване, за които ще ви разкажа по-надолу в статията.
  • Selenium Grid – дава възможност да се изпълняват тестове на отдалечени машини (виртуални или реални). Скриптовете се стартират от една локална машина (hub/server) и действията се изпълняват на множество отдалечени (nodes), които са регистрирани към нея. Това позволява да се тества паралелно на различни браузъри и операционни системи.

Въпреки че основното приложение на Selenium е автоматизирано тестване на уеб сайтове и приложения, можем да го използваме също за автоматизация на различни монотонни операции, както и за извличане на данни от Интернет (web scraping).

Кой език за програмиране, операционна система и браузър да използваме?

Когато тестваме уеб сайт със Selenium, имаме възможност да избираме между няколко езика за програмиране, браузъра и операционни системи. Това предоставя гъвкавост и удобство при работа. Точно кои от тях ще изберем е въпрос на лична преценка.

Езици за програмиране: Python, Java, Ruby, C#, JavaScript

Браузъри: Chrome, Firefox, Edge, Opera, Safari, Internet Explorer

Операционни системи: Windows, Linux, macOS

Има и възможност за работа с езици за програмиране, различни от изброените, като например PHP, Perl, R, Go и др., но поддръжката не се осъществява от официалния проект на Selenium.

Както споменах по-рано в статията, със Selenium Grid тестовете могат да се извършват паралелно на няколко машини с различни браузъри и операционни системи. Ако разполагате само с една машина, Selenium Webdriver дава също възможност за паралелно тестване на един или повече браузъра.

Също така може и да се тества под мобилни операционни системи, но са необходими допълнителни инструменти за тази цел. Такъв е Selenoid например, който се използва за устройства с Android, а за iOS е необходимо Selenium да се комбинира с Appium.

Какви са възможностите на Selenium Webdriver?

Selenium Webdriver позволява извършване на голям брой операции в браузъра. Може да се взаимодейства с елементите на уеб страницата, да се навигира из нея, да се въвеждат данни в текстови полета и др.

За примерите по-надолу ще използваме езика за програмиране Python.

Откриване на уеб елементи

Уеб елементите в Selenium представляват HTML етикетите в страницата (бутони, текст, линкове, изображения и др.). Можем да ги откриваме чрез метода find_element(), като е необходимо да зададем по какъв начин да търсим елемента. Това става с помощта на локатори (locators).

Selenium съдържа няколко локатора, чрез които да се откриват уеб елементи.

Пример:

<form id=login method=post action=https://examplewebsite.com/app/profiles/login>
    <div id=usr_name class=inputContainer>
        <input name=username type=text>      
        Username
    </div>
    <div id=pwd class=inputContainer>
        <input name=password autocomplete=off type=password>                 
        Password
    </div>
    <div id=loginButton>
        <input type=submit name=Submit class=button id=btnLogin value=LOGIN>
        <div id=forgotPwd>
            <a href=https://examplewebsite.com/app/profiles/forgottenPassword>Forgot your password?</a>
        </div>
    </div>
</form>

Следният HTML код е за създаване на форма за вход в потребителски профил.

ЛокаторПриложение
Наименование на клас (class name)driver.find_element(By.CLASS_NAME, 'inputContainer')
CSS селекторdriver.find_element(By.CSS_SELECTOR, '#login > div:nth-child(2)')
ID атрибутdriver.find_element(By.ID, 'usr_name')
NAME атрибутdriver.find_element(By.NAME, 'username')
Текст на хипервръзка (link text)driver.find_element(By.LINK_TEXT, 'Forgot your password?')
Частичен текст на хипервръзка (partial link text)driver.find_element(By.PARTIAL_LINK_TEXT, 'password')
HTML етикетdriver.find_element(By.TAG_NAME, 'a')
XPATHdriver.find_element(By.XPATH, '//div[@id="usr_name"]/input')

Таблицата представя локаторите, с които могат да се достъпват елементите от Selenium Webdriver. Точно кой ще използваме зависи от атрибутите на HTML елементите. Препоръчително е да се прилагат онези локатори, с които най-лесно да се открие търсеният елемент и които по-рядко се променят. Такива са ID и Name например. В някои ситуации обаче е нужно да открием повече от един елемент. Тогава е по-подходящо да се използва наименование на клас. Останалите локатори се променят по-често и при всяко изменение на уеб сайта може да е необходимо да се задават нови стойности в кода, иначе не може да бъде открит елементът при изпълнение на скрипта.

Действия с мишка и клавиатура

Със Selenium Webdriver могат да се изпълняват всички стандартни операции с мишка и клавиатура за взаимодействие с уеб елементите. Включени са като методи на класа ActionChains всякакви видове кликове, drag-and-drop, преместване до конкретна позиция на страницата и скролиране. Когато се създаде обект от този клас и се извикат конкретни методи, те първо се съхраняват и после изпълняват последователно чрез метода perform().

<ul id=mainMenuFirstLevelUnorderedList class=main-menu-first-level-unordered-list main-menu-first-level-unordered-list-width>
    <li class=main-menu-first-level-list-item>
        <a href=/index.php/admin/viewAdminModule id=menu_admin_viewAdminModule class=firstLevelMenu>
            <b>Admin</b>
        </a>
    </li>
    <ul>
        <li>
            <a href=# id=menu_admin_UserManagement class=arrow>User Management</a>
        </li>
        <li>
            <a href=/index.php/admin/viewSystemUsers id=menu_admin_viewSystemUsers>Users</a>
        </li>
    </ul>
</ul>

Източник: https://opensource-demo.orangehrmlive.com

# Откриване на уеб елементи
admin = self.driver.find_element(By.ID, 'menu_admin_viewAdminModule')
usr_management = self.driver.find_element(By.ID, 'menu_admin_UserManagement')
users = self.driver.find_element(By.ID, 'menu_admin_viewSystemUsers')

# Създаване на ActionChains обект
actions = ActionChains(self.driver)

# Изпълнение на конкретни действия върху откритите уеб елементите
actions.move_to_element(admin).move_to_element(usr_management).move_to_element(users).click().perform()

Следният пример представя достигане до конкретен елемент и клик върху него след това.

ActionChains се използва и при действия с клавиатура, ако искаме да направим конкретни клавишни комбинации. Класът Keys дава възможност за изпълнение на повечето клавиши на клавиатурата.

# Създаване на ActionChains обект
actions = ActionChains(self.driver)

# Изпълнение на конкретни действия
actions.key_down(Keys.CONTROL).send_keys('a').perform()

В този пример се изпълнява клавишната комбинация CTRL + A.

Друг пример за действия с клавиатура е да въведем текст в определено текстово поле при попълване на формуляр за регистрация или при влизане в потребителски профил.

<div id=usr_name class=inputContainer>
    <input name=username type=text>
    Username
</div>
driver.find_element(By.NAME, 'username').send_keys('user11')

В този пример първо се открива текстовото поле, след което му се задава стойността user11. За еднократно действие върху уеб елемент не е необходимо да използваме ActionChains.

Навигация

Преминаването от една уеб страница към друга със Selenium Webdriver се случва лесно. С помощта на локаторите се откриват навигационни елементи (бутони, хипервръзки и др.) и се изпълнява върху тях метода click(). Има също така и навигационни команди, които позволяват връщане на предишна страница, отиване на друг прозорец на браузъра и др.

<div id=loginButton>
    <input type=submit name=Submit class=button id=btnLogin value=LOGIN>
</div>
# Клик върху бутон
driver.find_element(By.ID, 'btnLogin').click()

# Връщане към предишна страница
driver.back()

# Навигиране към съседния прозорец в браузъра
window_after = driver.window_handles[1]
driver.switch_to_window(window_after)

В този пример е представен клик върху уеб елемент, който притежава ID със стойност btnLogin, връщане към предишна уеб страница и навигиране към съседен прозорец на браузъра.

Тестови проверки

Проверките са начинът, по който разбираме дали даден тест е успешен. Ако кодът ни е на Python, е необходимо да използваме модула unittest или pytest заедно със Selenium Webdriver.

При unittest има отделни функции за всяка проверка, като те могат да бъдат разделени на 3 категории: основни, сравнителни и за колекции.

ОсновниСравнителниЗа колекции
assertTrueassertEqualassertListEqual
assertFalseassertNotEqualassertTupleEqual
assertIsassertGreaterassertSetEqual
assertIsNotassertGreaterEqualassertDictEqual
assertIsNoneassertLessassertIn
assertIsNotNoneassertLessEqualassertNotIn
assertIsInstance
assertIsNotInstance

При pytest е доста по-улеснено, тъй като се използва само assert и изразът след него. Например assert a == b. Ако условието не е изпълнено, ще получим като резултат False и тестът ще бъде неуспешен.

Изключения и изчакване

По време на работа със Selenium Webdriver може да се случи така, че например стойността, която очакваме, да не е тази, която трябва, при което получаваме грешка. Може и също така някой елемент да не е вече на страницата и да не може да бъде открит или да няма достатъчно време да се извърши конкретна операция. Тогава получаваме изключение.

За да се избегнат такива случаи, често се налага да задаваме на Selenium Webdriver определено време за изчакване преди да се извърши конкретно действие. Предоставени са три начина за изчакване – неявно (implicit), явно (explicit) и плавно (fluent).

При неявното, Selenium Webdriver изчаква определено време докато даден елемент или елементи се появят в документния обектен модел (DOM).

<div id=pwd class=inputContainer>
    <input name=password autocomplete=off type=password>                 
    Password
</div>
# Задаване на 10 секунди време за изчакване
driver.implicitly_wait(10)

# Зареждане на уеб сайт
driver.get('https://opensource-demo.orangehrmlive.com/')

# Търсене на елемент
element = driver.find_element(By.ID, 'pwd')

В този пример задаваме времето за изчакване да е 10 секунди. Ако не е открит елемент до тогава, ще получим изключение за неоткрит елемент (NoSuchElementException).

Явните изчаквания се използват само за елементи, върху които след това трябва да се извърши действие.

# Изчакване 10 секунди докато може да се кликне върху уеб елемента
subscriber_btn = WebDriverWait(self.driver, 10).until(EC.element_to_be_clickable((By.ID, 'pwd')))

# Клик върху уеб елемента
subscriber_btn.click()

Класът ExpectedConditions (EC) дава възможност да задаваме определени критерии за уеб елементите. В нашия пример сме задали това да е възможността да се кликне върху елемента. Selenium Webdriver изчаква 10 секунди докато този критерий бива изпълнен.

Плавните изчаквания включват в себе си определено време на изчакване и при тях периодично се проверява дали даденият елемент е наличен.

# Изчакване 10 секунди докато може да се кликне върху уеб елемента
subscriber_btn = WebDriverWait(self.driver, 10, poll_frequency=1).until(EC.element_to_be_clickable((By.ID, 'pwd')))

Също както в предния пример се изчаква 10 секунди, но на всяка секунда се прави проверка дали върху елементът може да се кликне. Това се задава чрез параметъра poll_frequency.

Във всички случаи, веднага щом е открит елемент, изчакването приключва и Selenium Webdriver продължава нататък с действията, които трябва да извърши.

Генериране на доклад с резултати от тестовете

Доклади с информацията за преминалите тестове можем да получим с помощта на модула pytest. Той се генерира като HTML файл и в него имаме данни за статуса на тестовете, тяхната продължителност и за всякакви проблеми, които могат да възникнат.

selenium testing pytest report

Освен pytest има и други модули като allure например, с които може да се генерират доклади.

Защо да използваме Selenium Webdriver?

За разлика от много други инструменти за автоматизирано тестване, със Selenium Webdriver се работи изключително лесно, можем да избираме между няколко езика за програмиране, на които да пишем, както и ни предоставя възможност за междуплатформено и паралелно изпълнение на тестовете. С малко написан от нас код могат да се постигнат доста добри резултати, а когато има промени по уеб сайта, който се тества, организацията на проектите позволява много бързо да се направят необходимите корекции в скриптовете. И не на последно място, продуктът е безплатен и със свободен достъп.

Искате да научите повече за Python?

Включете се в курса по програмиране с Python.

Научете повече

Автор: Десислава Христова