Selenium to zestaw narzędzi pozwalających na automatyzację przeglądarki internetowej. Obsługuje wszystkie dostępne na rynku przeglądarki. Współpracuje z następującymi językami: Python, Java, CSharp, Ruby, Kotlin, JavaScript.
Kod z wyjaśnieniem. Page Object Pattern.
Napisałam dla Ciebie kod w Pythonie, który testuje stronę internetową w dwóch przeglądarkach: Chrome i Firefox. Poniższy kod uruchamia przeglądarkę, otwiera stronę https://testuj.pl, akceptuje ciasteczka, przechodzi do zakładki szkolenia, wyszukuje szkolenia, które w nazwie mają słowo Selenium, klika w Wypełnij test predyspozycji.
Test automatyczny w Firefox
from selenium.webdriver.support import expected_conditions from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.common.by import By from webdriver_manager.firefox import GeckoDriverManager from selenium.webdriver.firefox.service import Service as FirefoxService from selenium import webdriver def test_Firefox(): driver = webdriver.Firefox(service=FirefoxService(executable_path=GeckoDriverManager().install())) driver.get('https://testuj.pl/') assert 'testuj.pl' in driver.title driver.maximize_window() wait = WebDriverWait(driver, 10) cookie = (By.CSS_SELECTOR, "[class='cookie']") wait.until(expected_conditions.element_to_be_clickable(cookie)) driver.find_element(By.CSS_SELECTOR, "[class='agree right']").click() driver.find_element(By.CSS_SELECTOR, "[class='button']").click() assert driver.find_element(By.CLASS_NAME, 'coursesListHeader__list-url').is_displayed() driver.find_element(By.CSS_SELECTOR, "[name='name']").send_keys('Selenium') driver.find_element(By.CSS_SELECTOR, "[class='formField__button']").click() driver.quit() def test_testnatestera_Firefox(): driver = webdriver.Firefox(service=FirefoxService(executable_path=GeckoDriverManager().install())) driver.get('https://testuj.pl/') wait = WebDriverWait(driver, 10) cookie = (By.CSS_SELECTOR, "[class='cookie']") wait.until(expected_conditions.element_to_be_clickable(cookie)) driver.find_element(By.CSS_SELECTOR, "[class='agree right']").click() driver.maximize_window() driver.find_element(By.CSS_SELECTOR, "[class='button button--bigger']").click() driver.quit()
Test automatyczny w Chrome
from selenium.webdriver.support import expected_conditions from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.common.by import By from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.service import Service as ChromeService from selenium import webdriver def test_Chrome(): driver = webdriver.Chrome(service=ChromeService(executable_path=ChromeDriverManager().install())) driver.get('https://testuj.pl/') assert 'testuj.pl' in driver.title driver.maximize_window() wait = WebDriverWait(driver, 10) cookie = (By.CSS_SELECTOR, "[class='cookie']") wait.until(expected_conditions.element_to_be_clickable(cookie)) driver.find_element(By.CSS_SELECTOR, "[class='agree right']").click() driver.find_element(By.CSS_SELECTOR, "[class='button']").click() assert driver.find_element(By.CLASS_NAME, 'coursesListHeader__list-url').is_displayed() driver.find_element(By.CSS_SELECTOR, "[name='name']").send_keys('Selenium') driver.find_element(By.CSS_SELECTOR, "[class='formField__button']").click() driver.quit() def test_testnatestera_Chrome(): driver = webdriver.Chrome(service=ChromeService(executable_path=ChromeDriverManager().install())) driver.get('https://testuj.pl/') wait = WebDriverWait(driver, 10) cookie = (By.CSS_SELECTOR, "[class='cookie']") wait.until(expected_conditions.element_to_be_clickable(cookie)) driver.find_element(By.CSS_SELECTOR, "[class='agree right']").click() driver.maximize_window() driver.find_element(By.CSS_SELECTOR, "[class='button button--bigger']").click() driver.quit()
Przygotowanie do pisania testów
Pierwszym krokiem jaki wykonałam było pobranie bibliotek: Selenium, Webdriver-Manager oraz Pytest. Poniżej przedstawiam screen z wyszukiwania Webdriver - Manager.
Krok drugi to instalacja sterowników wybranych przeglądarek i rozpoczęcie sesji. Pamiętaj, że linie kodu z instrukcjami from… import… należy umieścić na górze strony, nad testem.
Firefox
from selenium.webdriver.firefox.service import Service as FirefoxService from webdriver_manager.firefox import GeckoDriverManager from selenium import webdriver driver = webdriver.Firefox(service=FirefoxService(executable_path=GeckoDriverManager().install()))
Chrome
from selenium.webdriver.chrome.service import Service as ChromeService from webdriver_manager.chrome import ChromeDriverManager from selenium import webdriver driver = webdriver.Chrome(service=ChromeService(executable_path=ChromeDriverManager().install()))
Sterowniki obsługują komunikację między przeglądarką a Selenium.
Wytłumaczenie kodu
Zwróć uwagę na to, że poniższy kod będzie wyglądał identycznie w testach na Firefox i Chrome.
Otworzenie strony internetowej https://testuj.pl
driver.get('https://testuj.pl/')
Na zmiennej driver wywołuję metodę get(). Wartość parametru, czyli zawartość nawiasu to nazwa strony, którą zamierzam otworzyć.
Asercje w Selenium
assert 'testuj.pl' in driver.title
Weryfikuje, czy w tytule strony jest tekst 'testuj.pl'.
assert self.driver.find_element(By.CLASS_NAME, 'coursesListHeader__list-url').is_displayed()
Sprawdza, czy klasa o nazwie 'coursesListHeader__list-url' wyświetla się na stronie.
Asercje są bardzo ważne podczas pisania testów. Tworząc asercje możesz porównać rzeczywisty wynik z oczekiwanym lub sprawdzić informacje o wybranym elemencie, na stronie. Dzięki nim sprawdzisz czy jesteś na właściwej stronie, czy dany element jest na niej dostępny, czy posiada określony tekst. Wynik asercji to prawda(True) lub fałsz(False).
Przykładowe informacje o elemencie:
- is displayed - czy jest wyświetlony
- is enabled - czy jest włączony
- is selected - czy jest wybrany, dotyczy np. pól wyboru, radio buttons
Maksymalizacja okna przeglądarki w Pythonie
driver.maximize_window()
Oczekiwanie w Selenium (wait)
Czekamy na załadowanie elementu na stronie, czyli jego pojawienie się, inaczej zaistnienie w modelu DOM. Jeśli element nie zdąży załadować się Python zgłosi wyjątek NoSuchElementException.
from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions wait = WebDriverWait(driver, 10) cookie = (By.CSS_SELECTOR, "[class='cookie']") wait.until(expected_conditions.element_to_be_clickable(cookie))
Klasę By należy zaimportować. Metody w klasie lokalizują element. Zaimportować należy także WebDriverWait i expected_conditions.
Chcąc swobodnie poruszać się po stronie, zamierzam zaakceptować pliki cookie. Powyższy kod mówi, że poczekam maksymalnie 10 sekund, do czasu pojawienia się możliwości kliknięcia w okno, zawierające klasę, o nazwie 'cookie'.
Sposoby oczekiwania w Selenium:
1.ImplicitWait – niejawny czas oczekiwania. WebDriver odpytuje DOM przez określony czas. Przykład:
implicitly_wait(10)
2. ExplicitWait – jawny czas oczekiwania. Webdriver odpytuje DOM do momentu spełnienia warunku, przez określony czas. Przykład:
timeout=10.until(some_condition
Przykładowe zdarzenia, na które możemy poczekać:
3. FluentWait – płynne oczekiwanie. WebDriver odpytuje DOM przez określony czas, w ustalonej przez nas częstotliwości, ignorując pewne typy wyjątków. Przykład:
timeout=10, poll_frequency=1, ignored_exceptions=[ElementNotVisibleException]
Kod, który znajduje element na stronie i wybiera go (klika)
Podczas testów klikam najpierw przycisk akceptujący ciasteczka
driver.find_element(By.CSS_SELECTOR, "[class='agree right']").click(),
następnie element o nazwie Szkolenia
driver.find_element(By.CSS_SELECTOR, "[class='button']").click(),
lupę podczas szukania szkolenia o nazwie Selenium
driver.find_element(By.CSS_SELECTOR, "[class='formField__button']").click()
i pole Test predyspozycji
driver.find_element(By.CSS_SELECTOR, "[class='button button--bigger']").click().
Lokalizatory, pozwalające odwołać się do elementu:
- Class name
- CSS selector
- ID
- Name
- Link text
- Partical link text
- Tag name
- Xpath
Wybrałam CSS selector, bo lubię.
'agree right', 'button', 'formField__button', 'button button--bigger' są to selektory.
Szukanie selektora w DevTools
Otwórz konsolę programistyczną, naciskając F12, fn + F12 lub prawym przyciskiem myszy element, którego selektor chcesz poznać i wybierz Zbadaj/Inspect.
W sekcji Inspector (Firefox)/ Elementy (Chrome) widzisz jak zostało zbudowane dane pole.
Możesz podejrzeć dane sąsiadujących elementów, poruszając się strzałkami w górę i w dół.
Teraz naciśnij przycisk inspekcji kodu (kwadrat z kursorem) i wskaż myszką interesujący Cię, dowolny element na stronie.
Użyj selektor, który jest niepowtarzalny. Wybierz identyfikator (accessibility id, id) jeśli jest dostępny.
Na elementach możesz wykonać następujące polecenia:
- click
- send keys lub clear (przy polach tekstowych i elementach edytowalnych)
- select (przy liście elementów)
Wyszukanie wybranego słowa na stronie, przy pomocy lupy
driver.find_element(By.CSS_SELECTOR, "[name='name']").send_keys('Selenium')
Powyższy kod zawsze będzie wyszukiwał słowo 'Selenium'.
Zamknięcie przeglądarki
driver.quit()
Fixtures
Z pewnością zauważyłaś/eś, że znaczna część kodu powtarza się w kolejnych testach. Rozwiązaniem na powtórzenia jest dekorator @pytest.fixture. Opowiedziałam Ci o nim w artykule https://testuj.pl/blog/testy-z-uzyciem-frameworka-pytest-fixture-i-parametryzacja-testu/. Przeczytaj go, proszę jeśli chcesz zrozumieć dalszą część artykułu. Poniżej użyję również klasę Pythonową. We wspomnianym artykule, znajdziesz także wytłumaczenie klas.
Zerknij ponownie na testy w Chrome
from selenium.webdriver.support import expected_conditions from selenium.webdriver.support.wait import WebDriverWait from selenium.webdriver.common.by import By from webdriver_manager.chrome import ChromeDriverManager from selenium.webdriver.chrome.service import Service as ChromeService from selenium import webdriver def test_Chrome(): driver = webdriver.Chrome(service=ChromeService(executable_path=ChromeDriverManager().install())) driver.get('https://testuj.pl/') assert 'testuj.pl' in driver.title driver.maximize_window() wait = WebDriverWait(driver, 10) cookie = (By.CSS_SELECTOR, "[class='cookie']") wait.until(expected_conditions.element_to_be_clickable(cookie)) driver.find_element(By.CSS_SELECTOR, "[class='agree right']").click() driver.find_element(By.CSS_SELECTOR, "[class='button']").click() assert driver.find_element(By.CLASS_NAME, 'coursesListHeader__list-url').is_displayed() driver.find_element(By.CSS_SELECTOR, "[name='name']").send_keys('Selenium') driver.find_element(By.CSS_SELECTOR, "[class='formField__button']").click() driver.quit() def test_testnatestera_Chrome(): driver = webdriver.Chrome(service=ChromeService(executable_path=ChromeDriverManager().install())) driver.get('https://testuj.pl/') wait = WebDriverWait(driver, 10) cookie = (By.CSS_SELECTOR, "[class='cookie']") wait.until(expected_conditions.element_to_be_clickable(cookie)) driver.find_element(By.CSS_SELECTOR, "[class='agree right']").click() driver.maximize_window() driver.find_element(By.CSS_SELECTOR, "[class='button button--bigger']").click() driver.quit()
Teraz zobacz, jak będzie wyglądał kod, z użyciem dekoratora fixture
import pytest @pytest.fixture() def driver(): driver = webdriver.Chrome(service=ChromeService(executable_path=ChromeDriverManager().install())) driver.get('https://testuj.pl/') assert 'testuj.pl' in driver.title driver.maximize_window() wait = WebDriverWait(driver, 10) cookie = (By.CSS_SELECTOR, "[class='cookie']") wait.until(expected_conditions.element_to_be_clickable(cookie)) driver.find_element(By.CSS_SELECTOR, "[class='agree right']").click() yield driver driver.quit() def test_Chrome(driver): driver.find_element(By.CSS_SELECTOR, "[class='button']").click() driver.find_element(By.CSS_SELECTOR, "[name='name']").send_keys('Selenium') driver.find_element(By.CSS_SELECTOR, "[class='formField__button']").click() def test_testnatestera_Chrome(driver): driver.find_element(By.CSS_SELECTOR, "[class='button button--bigger']").click()
Fixture dzieli się na 3 części. Pierwsza część wykona się przed testem. Będzie to uruchomienie przeglądarki, otworzenie strony, maksymalizacja okna i akceptacja ciastek. Druga to yield, czyli zwróć drivera. Trzecia część wykona się po teście. W naszym przypadku będzie to zamknięcie przeglądarki. Od teraz nasze testy zawierają tylko akcje na stronie.
Page Object Pattern
Wzorzec projektowy polegający na odizolowaniu testów od akcji, które wykonujemy na elementach. W POP jedna strona internetowa odpowiada jednej klasie, a każda klasa znajduje się w osobnym pliku.
Przekładając zasadę na nasz kod:
- pierwszy plik, pierwsza klasa - akcje na stronie głównej testuj.pl, czyli akceptacja ciastek, kliknięcie w Szkolenia i Wypełnij test predyspozycji
- drugi plik i klasa w nim zawierałaby akcje na drugiej stronie, otworzonej po kliknięciu w zakładkę Szkolenia. Akcja jaką wykonałam na stronie to wyszukanie w lupce kursów, które w nazwie mają słowo Selenium
- trzeci plik i kolejna klasa odnosiłaby się do strony, otwartej po kliknięciu Wypełnij test predyspozycji
Celem POP jest łatwiejsza modyfikacja kodu w przyszłości. Pliki z klasami powinny być zapisane w folderze o nazwie Pages, natomiast testy w folderze Tests.
Przebuduję kod zgodnie z Page Object Pattern. Użyję przeglądarki Chrome
Stworzyłam nowy plik o nazwie first_page a w nim klasę o nazwie FirstPage. Klasa zawiera akcje jakie wykonam na pierwszej stronie, czyli czekanie aż okno ciastek będzie klikalne, akceptację ciastek, kliknięcie w Szkolenia i Wypełnij test.
from selenium.webdriver.common.by import By from selenium.webdriver.support import expected_conditions from selenium.webdriver.support.wait import WebDriverWait class FirstPage: def __init__(self, driver): self.driver = driver def wait_for_cookie(self): wait = WebDriverWait(self.driver, 10) cookie = (By.CSS_SELECTOR, "[class='cookie']") wait.until(expected_conditions.element_to_be_clickable(cookie)) def accept_cookie(self): self.driver.find_element(By.CSS_SELECTOR, "[class='agree right']").click() def go_to_training(self): self.driver.find_element(By.CSS_SELECTOR, "[class='button']").click() assert self.driver.find_element(By.CLASS_NAME, 'coursesListHeader__list-url').is_displayed() def go_to_test(self): self.driver.find_element(By.CSS_SELECTOR, "[class='button button--bigger']").click()
Po kliknięciu zakładki Szkolenia, uruchomię drugą stronę. Tworzę więc drugi plik o nazwie second_page ,a w nim klasę SecondPage. W klasie znajduje się metoda wyszukująca szkolenia, które w nazwie mają słowo Selenium.
from selenium.webdriver.common.by import By class SecondPage: def __init__(self, driver): self.driver = driver def find_training(self): self.driver.find_element(By.CSS_SELECTOR, "[name='name']").send_keys('Selenium') self.driver.find_element(By.CSS_SELECTOR, "[class='formField__button']").click()
Do pliku testowego zaimportuje stworzone klasy. Zerknij, jak teraz wyglądają testy.
from first_page import FirstPage from second_page import SecondPage @pytest.fixture() def driver(): driver = webdriver.Chrome(service=ChromeService(executable_path=ChromeDriverManager().install())) driver.get('https://testuj.pl/') assert 'testuj.pl' in driver.title driver.maximize_window() yield driver driver.quit() def test_first_page(driver): first_page = FirstPage(driver) first_page.wait_for_cookie() first_page.accept_cookie() first_page.go_to_training() first_page.go_to_test() def test_second_page(driver): first_page = FirstPage(driver) first_page.wait_for_cookie() first_page.accept_cookie() first_page.go_to_training() second_page = SecondPage(driver) second_page.find_training()
Test pierwszy wykona operacje na stronie https://testuj.pl/. Z fixture pobierze otwarcie przeglądarki i strony, maksymalizację ekranu. Następnie użyję klasę o nazwie FirstPage, z pliku first_page do czekania na okno ciastek, akceptacji ciastek, kliknięcia zakładek Szkolenia i Wypełnij test.
Test drugi wykona operacje na stronie https://testuj.pl/lista-szkolen. Będzie to możliwe po ponownym wykonaniu kroków z testu pierwszego,a następnie odwołaniu do klasy SecondPage, z pliku second_page. Na drugiej stronie przetestuję wpisanie nazwy Selenium w lupę i wyszukaniu szkoleń zawierających tą nazwę.
Podsumowanie
W artykule opowiedziałam Ci o testach automatycznych w Selenium. Językiem programowania, który użyłam był Python. W ostatecznej wersji kodu zastosowałam fixture i Page Object Pattern. Podpowiedziałam Ci jak poczekać na element, kliknąć w niego i wyszukać konkretne słowo. W artykule opisałam także znajdowanie selektorów w DevTools.
Życzę Ci powodzenia w pisaniu testów automatycznych.