Wybierz region
pl
  • PL
  • EN
Wydrukuj

Page Object Model w Selenium WebDriver – jak zadbać o koszty utrzymania kodu testów automatycznych

W tym wpisie chciałbym się przyjrzeć automatyzacji testów w JAVA przy użyciu frameworka Selenium WebDriver wraz z zastosowaniem Page Object Model.

Krótko o Selenium WebDriver mogę napisać, że jest to API pozwalające nam na wykonywanie operacji na elementach strony. Działanie polega na „wstrzyknięciu” (sic!) javascriptu do drivera, w którym otwieramy interfejs strony. Daje nam to szerokie możliwości i dowolną konfigurację przypadków testowych.

Ogromnym plusem automatyzacji testów jest założenie, że przy pewnym nakładzie pracy. Testy powtarzalne np. regresje, jesteśmy w stanie wykonać szybciej. Jeżeli dołożymy do tego jeszcze Continues Integrations to błędy możemy wyłapać zaraz po wypchnięciu kodu przez programistę. Niestety ten „pewien nakład pracy” zwiększa się wraz z każdą zmianą w kodzie, która może bezpośrednio wpłynąć na zmiany w GUI. Takie zmiany to:

zmiana id w elementach formularza, po którym identyfikujemy dany element strony

lub

dodanie paragrafu na stronie przed elementem, który identyfikujemy po css
Dlatego utrzymanie naszych testów powinno być jak najmniejszym kosztem i tutaj z pomocą przychodzi nam Page Object Model. Pamiętam jak zaczynałem stawiać pierwsze kroki w automatyzacji testów, dużą trudność sprawiało nanoszenie mi poprawek w kodzie. Wynikało to głównie z tego, że miałem pomieszaną logikę wraz z implementacją testów.

W Page Object Model motywem przewodnim jest oddzielenie klas posiadających elementy strony oraz metody na niej wykonywanych (Pages), z klasami posiadającymi scenariusze testowe do wykonania (Tests).

Na potrzeby tego wpisu wykonałem testy znanej wszystkim strony Booking.com. Podszedłem do tego tematu w ten sposób, ponieważ uważam, że najlepiej uczyć się na konkretnych przykładach i konkretnych problemach. Cały kod znajdziemy na githubie (link).

Zacznijmy od utworzenia nowego projektu Maven. W pliku pom.xml dodajemy dependencje seleniumhq, dzięki czemu będziemy mogli korzystać ze wszystkich zasobów Selenium API. Następnie dodamy bilbliotek TestNg, dzięki której będziemy mogli zautomatyzować nasze testy.

Struktura projektu wygląda w ten sposób:

 

 

Tak jak już pisałem w „pages” będziemy przetrzymywać wszystkie klasy stron a w „tests” wszystkie klasy, w których będziemy implementować scenariusze testowe.

Zaczniemy od Pages klasy nadrzędnej, w której zdefiniujemy konstruktora i który będzie wymuszał na nas inicjalizowanie dwóch zmiennych driver z klasy WebDriver i wait z klasy WebDriverWait. Trzecim elementem naszego konstruktora to wywołanie metody initElements na obiekcie PageFactory. To nam daję między innymi możliwość deklarowania WebElementów adnotacją @FindBy lub @FindBys oraz @FindAll (kiedy jest ich więcej niż jeden). Dzięki czemu te elementy są inicjalizowane statyczną metodą InitElements co ułatwia nam korzystanie z zasobów Selenium bez konieczności przekazywania obiektu WebDriver-a w każdej metodzie.

 

Następną klasą, po której będą dziedziczyć testy, jest klasa BaseTest. W tej klasie, w metodzie setup umieścimy kod odpowiedzialny za inicjalizacje WebDrivera. W naszym przypadku jest to chromedriver.exe i znajduje się w głównym katalogu naszego projektu. Ważne jest, aby ustawić property używając metody System.setProperty ze wskazaniem na nasz chromedriver.exe. Następne linijki inicjalizują naszego drivera oraz waita, który pomoże nam w czekaniu na elementy. Dzięki adnotacji TestNg @BeforeClass metoda setUp będzie wywoływana przed każdą klasą więc w implementacje jej możemy wpleść jeszcze metody takie jak np. maksymalizacja okna drivera lub zalogowanie się na stronę, którą będziemy testować. Wszystko zależy od naszych potrzeb.

Korzystam również z adnotacji @AfterClass w metodzie, która jest odpowiedzialna za zamykanie okna drivera po wykonaniu testu.

Z tak przygotowaną strukturą możemy rozpocząć pisanie testów automatycznych.

Spójrzmy na klasę MainPage:

Jak widać dziedziczymy po klasie BasePage i wywołujemy konstruktora klasy nadrzędnej, dzięki czemu nie musimy się martwić inicjalizacją drivera.

Dzięki adnotacjom @FindBy mamy wszystkie elementy strony, które będą nam potrzebne do przeprowadzenia testu w jednym miejscu. Dzięki temu nanoszone zmiany, które się zdarzają przy implementacji nowych rozwiązań nie wymagają od nas dużej ingerencji w kod, aby nanieść poprawki. Warto też zwrócić uwagę na nazwy zmiennych, aby konkretnie wskazywały, o jaki element strony nam chodzi. Czy jest to input, button czy może lista wartości?

Następną rzeczą, na którą warto zwrócić uwagę jest nazewnictwo metod, które jasno określają, za jakie czynności odpowiadają. Dobrą praktyką jest również, aby metody były odpowiedzialne za jak najmniejszą ilość funkcji, ponieważ ułatwia to wprowadzanie jakichkolwiek zmian w przyszłości.

Tutaj dla przykładu mamy LoginPage:

 

Przykład użycia tych dwóch klas w teście mamy w BaseTest gdzie dodaliśmy mechanizm logowania.

Przykład implementacji wyszukiwania apartamentu w Zakopanem w klasie SerchingTest:

Tak spreparowane testy możemy uruchamiać z możliwości jaką dostarcza nam TestNg a mianowicie z testng.xml. W tym xml-u możemy skonfigurować nasze test suits tak jak tego chcemy i testy mogą być uruchamiane z linii komend.

Wszystkie klasy tutaj użyte możemy znaleźć w package - pages a cały kod znajdziemy w repo na github-ie. Wpis ten jest oczywiście małym przykładem tego, jak możemy wprowadzić Page Object Model do naszych projektów testów automatycznych.


Kajetan Lipiński

Pracuję w Pionie Energetyki i Gazownictwa, do moich głównych zadań należy kontrola jakości wypuszczanych produktów oraz automatyzacja testów aplikacji webowych oraz desktopowych. Zainteresowania to automatyzacja oraz optymalizacja procesów wytwórczych w IT.


Wydrukuj