AKTUALIZACJA
Najnowszą wersję tego wpisu w formie profesjonalnego (i na obecną chwilę darmowego) kursu znajdziesz na moim autorskim portalu https://jaktestowac.pl/kursy/.
Pozdrawiam Przemek
Wstęp
Wzorując sie na podstawowym teście stworzonym w poprzednim poście http://przemek.yum.pl/testy-automatyczne-selenium-cz2-pierwsze-kroki-w-pythonie/ można napisać kolejne. Jednak pierwsza asercja, która zakończy się niepowodzenie niestety przerwie cały test i kolejne nie będą już wykonywane – mozna to zuważyć choćby po niezamkniętym oknie przeglądarki.
UWAGA:
Poprzednia wersja tego wpisu był pisana w Python3 obecnie jest w wersji Python2 ze względu na biblioteki do testowania które w przyszłości chciałem opisać. Nie podmieniałem screenów bo są to dość kosmetyczne zmiany. Proszę o wzorowanie się na samym tekście kodu z pisanych przykładów bo te na screenach są w Python3 . Zmiany dotyczą rozwiązania z obsługą kodowania znaków: w Python3 nie jest potrzebna pierwsza linia odnośnie kodowania jak i znaczek u przed apostrofem w stringach (ergo – zastosowano domyślnie rozpoznawanie kodowania znaków specjalnych). Pamiętaj więc o literce u przed każdym łańcuchem zawierającym polskie znaki.Dodatkowo uaktualniłem obecny tytuł strony olx 🙂
Wymagania
Do zbudowania fajnie wyglądającego przypadku testowego należy opanować trochę wiedzy z programowania obiektowego – jednak generalnie zostanie tu wszystko opisane. J Zbudujemy klasę testową która bedzie posiadać metody (warto zapamiętać, że motoda to funkcja znajdująca się w klasie) będące osobnymi przypadkami testowymi.
Jak czegoś nie rozumiesz to na razie się tym nie przejmuj – leć z tutkiem dalej i potem przejdź go drugi raz i wtedy zobaczysz co się rozjaśniło czego trzeba się poduczyć.
Piszemy automat
Na początek tworzę nowy plik z testami olx_main_tests. Dodaję linie umożliwijąca kodowanie polskich znaków oraz importy:
# -*- coding: utf-8 -*- import unittest from selenium import webdriver
jak widać będe korzystał z biblioteki unittest, która oferuje wiele przydatnych metod do testowania.
Kodzimy – klasa, pierwszy test i uruchomienie
Deklaruję nazwę klasy korzystając ze słowa kluczowego class:
class OlxMainTests(unittest.TestCase):
korzysta ona z gotowych rozwiązań (dziedziczy) zaimplementowanych w bibliotece unittest
Następnie dodaję metodę która jest właśnie naszym testem korzystając ze słowa kluczowego def (self jest zawsze dodawane przy metodach):
Dodatkowo nazwę metody zaczynam od słowa test – dzięki czemu zostanie ona rozpoznana jako metoda testowa
def test_main_page(self):
Warto zaznaczyć, że w pythonie o przynależności danego wiersza do danej funkcji decyduje wielkość wcięcia. Dla przykładu powyższa deklaracja metody powinna posiadać jeden tabulator gdyż należy do klasy zdeklarowanej powyżej. To samo z ciałem metody, które wklejamy z naszego poprzedniego testu – powinno ono zawierać jedno wcięcie pod nazwą metody – czyli efekt końcowy to:
class OlxMainTests(unittest.TestCase): def test_main_page(self): driver = webdriver.Firefox() driver.get('http://olx.pl') title = driver.title print(title) assert title == u'Ogłoszenia - Sprzedam, kupię na OLX.pl' driver.close()
Można już uruchomić nowy test (nie zapomnij o nowej konfiguracji uruchumieniowej dla tego testu Run->Run..->Unittest OLxMainTest – gdyż w przeciwnym razie uruchomisz stary test)
Po uruchomieniu widać, że test został rozpoznany i powinien zakończyć się sukcesem
Dodanie kolejnego testu i uruchomienie grupy testów
Dodajemy następny test według posiadanego szablonu – sprawdzimy czy podstrona
'http://olx.pl/oferty/'
zawiera ten sam tytuł co główna. Po za zamianą adresu www należy utworzyć nową nazwę metody:
def test_oferty_page(self): driver = webdriver.Firefox() driver.get('http://olx.pl/oferty/') title = driver.title print(title) assert title == u'Ogłoszenia - Sprzedam, kupię na OLX.pl' driver.close()
Możemy nowy test uruchomić za pomocą nowej konfiguracji uruchomieniowej – jednak czas aby wykonac wszystkie testy razem – klik prawym klawiszem myszki na plik olx_main_tests.py i wybieram Run
Widać, że przeglądarka uruchomiła się dwukrotnie oraz wszystkie testy powinny przejść:
Dodatkowo można rozwinąć drzewko wykonanych testów:
Następny test i … mamy bug – rozpoznawanie problemu i naprawa testu
Dodam kolejny test – stronę: http://olx.pl/zasady/
ale tym razem ma on nie przechodzić – oto wynik:
Test niestety nie przeszedł. Jak można się zorientować po logu w konsoli – na tej podstronie znajduje się inny tytuł.
Czyżbyśmy znaleźli BUGA!
Ciężko stwierdzić – trzeba by było podpytać twórców aplikacji webowej OLX. Na obecna chwilę zakładam, że taki tytuł ma się wyświetlać więc naprawiam test – i powinno wszystko działać.
(HINT: Jeśli od twórców OLX otrzymałbym odp, że to jest błąd na stronie – wtedy nic nie poprawiam i pozostawiam mój test, który będzie tak długo nie przechodzł jak długo nie zostanie poprawiony błąd)
Refaktoryzacja – przeczyszczamy kod
Jak widać pojawia się sporo powtórzonego kodu – dodatkowo test który nie przeszedł pozostawił otwarta przeglądarkę – co by było gdyby tych testów było 1000?
Uniwersalny kod dla każdego testu w jednym miejscu
Na poczatek dodamy metodę, która wykona się przed każdym testem (umieszczam ją przed wszystkimi metodami):
def setUp(self): self.EXPECTED_MAIN_TITLE = u'Ogłoszenia - Sprzedam, kupię na OLX.pl'
Metoda setUp jest już zaimplementowana w bibliotece unittest link – jedynie rozszerzam jej działanie – dzięki czemu korzystam z tego, że unittest zna tę metodię i uruchamia się ona automatycznie przed każdym testem jednocześnie dodając moje warunki.
W metodzie umieściłem zmienną która przechowuje łańcuch znakowy (nazywamy string). Wielkie litery są użyte aby zaznaczyć, że jest to wartość której nie należy modyfikować poza tą metodą. Użycie self pozwala na dostanie się do tej zmiennej przez inne motody (ponieważ one wszystkie posiadają obiekt self) – czyli w innych metodach gdzie powtarza się wyciągnięty string można wprowadzić zmianę
assert title == self.EXPECTED_MAIN_TITLE
Pamiętaj aby uruchamiać testy po takich zmianach aby sprawdzić czy wszystko działa poprawnie.
Zarządzanie przeglądarką
Natepnie zajmuję zajmuję się zarządzaniem przeglądarką.
Dodaję do metody setUp inicjalizaję drivera
self.driver = webdriver.Firefox()
A w metodach zmieniam linię z inicjalizacją drivera:
driver = self.driver
Lokalna zmienna w metodzie driver otrzymuje referencję do obiektu self.driver. Można to potraktować jak przypisanie do obiektu wygodnego aliasu dzięki czemu nie trzeba za kazdym razem używać pełnej nazwy z self. Oczywiście można by było nie dodawac tego aliasu i tylko zmienić driver na self.driver (gdyż driver jest inicjalizowany w metodzie wywołującej się przed kazdym testem setUp) czyli:
def test_oferty_page(self): self.driver.get('http://olx.pl/oferty/') title = self.driver.title print(title) assert title == self.EXPECTED_MAIN_TITLE self.driver.close()
Będę się posługiwał formą z aliasem gdyz jest czytelniejsza:
def test_oferty_page(self): driver = self.driver driver.get('http://olx.pl/oferty/') title = driver.title print(title) assert title == self.EXPECTED_MAIN_TITLE driver.close()
W podobny sposób do metody wywoływanej przed kazdym testem setUp pozbywam się wywołania zamykania przeglądarki – dodając na końcu klasy metodę tearDown
def tearDown(self): self.driver.close()
Dzięki czemu mozna usunąć ze wszystkich metod wiersz z driver.close()
Testy w jednym oknie przeglądarki
Kolejną sprawę którą można zmienić to uruchamiająca się z każdym testem przegladarka – symulując zachowanie uzytkownika warto (w tym przypadku) zrobić testy na jednej instancji przeglądarki.
Dodatkowo zbadam czas wykonywania się wszystkich testów na osbnych instancjach przeglądarki – przyda się do tego opcja w oknie uruchamiania
klikam trybik i zaznaczam Show Statistic w zależności czy klikniemy na Test Result czy pojedynczy test wyświetli nam się czas wykonania całej grupy bądź poszczególnego testu.
Biblioteka unittest posiada metodę, która uruchomi się tylko raz przed testami dzięki temu można ją wykorzystac do uruchomienia przeglądarki. Przenoszę więć inicjalizajcę Firefoxa do nowej metody
@classmethod def setUpClass(self): self.driver = webdriver.Firefox()
Zauważmy, że zamykamy przeglądarkę po każdym teście – czyli drugi test zakończy się błędem gdyz przeglądarka nie będzie uruchomiona. Czas skorzystać z metody która wykona się po wszystkich testach tearDownClass – dopiero wtedy nastapi zamknięcie przeglądarki
# def tearDown(self): # self.driver.close() @classmethod def tearDownClass(self): self.driver.close()
Metoda tearDown nie jest nam na razie potrzebna . Gdy wytniem z niej wiersz z zamknieciem drivera zgłaszała by błąd. Można ją zakomentować jak w przykładzie powyżej lub tymczasowo zaślepić słowem pass albo usunąć:
def tearDown(self): pass
Teraz testy uruchamiają się na jednej instancji przeglądarki co wygląda jak prawdziwa akcja użytkownika.
Warto porównać czas wykonania dla testów w jednej i wielu instancjach przeglądarki:
Osobne przeglądarki: olx_main_tests.OlxMainTests | 20.32 s |
Jedna przeglądarka: olx_main_tests.OlxMainTests | 6.748 s |
Podsumowując:
Umiemy teraz napisać wiele testów sprawdzających tytuł strony
Testy mogą uruchomić się na jednej instancji przeglądarki
W testach są assercje co oznacza, że test automatycznie zakończy się niepowodzeniem gdy sprawdzany warunek okaże się błędny
Na koniec jeszcze wklejam cały kod z dzisiejszego posta
# -*- coding: utf-8 -*- import unittest from selenium import webdriver class OlxMainTests(unittest.TestCase): @classmethod def setUpClass(self): self.driver = webdriver.Firefox() def setUp(self): self.EXPECTED_MAIN_TITLE = u'Ogłoszenia - Sprzedam, kupię na OLX.pl' def test_main_page(self): driver = self.driver driver.get('http://olx.pl') title = driver.title print(title) assert title == self.EXPECTED_MAIN_TITLE def test_oferty_page(self): driver = self.driver driver.get('http://olx.pl/oferty/') title = driver.title print(title) assert title == self.EXPECTED_MAIN_TITLE def test_zasady_page(self): driver = self.driver driver.get('http://olx.pl/zasady/') title = driver.title print(title) assert title == 'Regulamin OLX.pl' def tearDown(self): pass @classmethod def tearDownClass(self): self.driver.close()
mozna prosic o jeszcze wiecej przykladow ?selenium + python ? np. page object pattern zastosowanie? switnie sie to czyta i jest bardzo pomocne
Sorki za późną odpowiedź ale nie sądziłem, że pod 2 letnim postem ktoś jeszcze napisze, ale miła niespodzianka.
Jeśli wciąż interesuje Ciebie ten temat i chcesz się w nim rozwijać to wbijaj na https://jaktestowac.pl/ na którym znajdziesz odp na powyższe pytania w jeszcze lepszej formie.
Pozdro
A cos sie będzie na tej stronce działo ? Bo narazie pusty bootstrap 😀
Będzie się działo – w pierwszych tygodniach planujemy przenosiny postów z tego bloga oraz ich upzupełnienie o nowe treści. Tak że w okolicach sierpnia/września możliwe, że trafią materiały z kontynuacją 🙂 wbijaj na https://jaktestowac.pl/
: )
Cześć, bazując na Twoim krótkim tutorialu nt. automatyzacji Pythonem, napisałem trochę skryptów do pracy, jednak natrafiłem na pewien problem, mianowicie oczekiwanie na pełne załadowanie strony , chodzi mi o pełne załadowanie całego contentu, nie ładowanie konkretnych webelementów. Dopiero zaczynam przygodę z Pythonem, w związku z tym prośba, czy mógłbyś na podstawie skryptów z tego tutorialu powiedzieć jak to powinno wyglądać ?
Osobiście korzytam z driver.implicitly_wait(10) – ale z tego co widzę to nijak tych 10 sek. nie czeka.
Bedę wdzieczny za każdą wskazówkę.
Hej – trochę późno ale zawsze 🙂
Ponieważ niektóre strony są asynchronicznie ładowane często występuje problem z czekaniem na dany element gdyż informacja dla metody
driver.implicitly_wait
jest taka, że strona już się załadowała ale niestety pewne dane wciąż na nią przybywają.Wtedy korzystamy z innego sposobu
Dodajemy import (jak inne importy)
from selenium.webdriver.support.ui import WebDriverWait
Potem robimy prostą funkcję (możesz przykleić kod)
def find(driver):
element = driver.find_elements_by_id("data")
if element:
return element
else:
return False
W tej metodzie zwróć uwagę na zmienną element – jej wartość to coś znalezionego w kodzie html testowanej strony.Szukanie elementów to temat na osobny post ale jeśli chcesz się dowiedzieć coś więcej teraz to zobacz tutaj http://selenium-python.readthedocs.io/locating-elements.html
Jeśli już masz szukany element to czas na użycie funkcji – tu właśnie powiesz ile sekund (ja dałem 10) ma twój program próbować aby i aktywnie szukać elementu
elementFound = WebDriverWait(driver, 10).until(find)
Powinno zadziałać
PS: Przenoszę bloga na (https://jaktestowac.pl/) gdzie do końca roku planuję dodanie materiałów do nauki testowania, automatyzacji i BDD dla początkujących i zaawansowanych testerów.
Cześć. Możesz podać adres Twojego bloga? Przy okazji.. Wiesz może gdzie znajdę najlepsze materiały aby ruszyć z pisaniem automatów? Java czy Python wg Ciebie jest lepszy? Pozdrawiam. Super post
Jasne – to już nie blog a profesjonalne kursy – za ok 2 tygodnie wypuszczamy XPath więc do dzieła – https://jaktestowac.pl
Witam,
mam pytanie. Skopiowałem cały kod z posta. I jak uruchamiam test, to nic się nie dzieje . Przeglądarka nie uruchamia się. W konsoli jest coś takiego:
C:\Users\Uzytkownik\AppData\Local\Programs\Python\Python36-32\python.exe “C:/Nowy folder (2)/selenium/zadanie_1.py”
Process finished with exit code 0
ps. Mała pomoc ?
Hej Łukasz
Wraz ze zmianami w nowym Selenium stara metoda nie działa – ale zobacz na nowej stronie i tam masz wszystko tak zrobione że zadziała od strzała – a jak nie to pisz. Wbijaj na https://jaktestowac.pl. Przy okazji powodzenia w automatach i pisz śmiało jak Ci idzie