Testy automatyczne Selenium cz3 – zestawy testów.

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
2015-05-03_100032

 

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ść:
2015-05-03_102503Dodatkowo można rozwinąć drzewko wykonanych testów:
2015-05-03_103114

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:
2015-05-03_111440Test 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 uruchamiania2015-05-03_124550

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()

11 thoughts on “Testy automatyczne Selenium cz3 – zestawy testów.

  1. mozna prosic o jeszcze wiecej przykladow ?selenium + python ? np. page object pattern zastosowanie? switnie sie to czyta i jest bardzo pomocne

    1. 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

        1. 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/

  2. 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ę.

    1. 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.

  3. 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

  4. 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 ?

    1. 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

Leave a Reply

Your email address will not be published. Required fields are marked *