W artukule JUnit 5 i Selenium - Odpowiedzialność metod testowych wprowadziłem zmiany, które poprawiły podział odpowiedzialności metod testowych w projekcie testów automatycznych dla aplikacji TodoMVC. Kolejnym ulepszeniem będzie wydzielenie odpowiednich metod prywatnych do oddzielnych klas i wprowadzenie wzorca Page Object.
O serii
Czytasz część 3 serii artykułów na temat automatyzacji testów aplikacji internetowej TodoMVC. Pozostałe części:
- Część 1 - JUnit 5 i Selenium - Wprowadzenie do projektu automatycznych testów aplikacji internetowej
- Część 2 - JUnit 5 i Selenium - Odpowiedzialność metod testowych
- Część 4 - JUnit 5 i Selenium - Optymalizacja konfiguracji i uruchomienia projektu
- Część 5 - JUnit 5 i Selenium - klasa PageFactory z pakietu support biblioteki Selenium
Kod źródłowy
Kod źródłowy opracowany w poprzednim artykule znajduje się w repozytorium Git: https://gitlab.com/qalabs/blog/junit5-selenium-todomvc-example w branchu 2-tests-responsibility. Klasa testowa, która zostanie zmodyfikowana w tym artykule, to TodoMvcTests, do jej kodu można przejść bezpośrednio po kliknięciu w link pl.qalabs.blog.junit5.selenium.todomvc.TodoMvcTests.
Rozdzielenie warstw
Po ostatnich zmianach w kodzie projektu możemy wyszczególnić trzy warstwy odpowiedzialności:
1 | // warstwa 1 - logika testu |
W kolejnych krokach usprawnię kod w taki sposób, aby poszczególne warstwy znalazły się w oddzielnych klasach i pakietach.
Logika Aplikacji - Page Object Pattern
Logika aplikacji (zawarta dotychczas w ogólnej klasie TodoMvcTests, z grubsza pomiędzy liniami 130 i 210 zostanie wyniesiona do klasy odpowiedzialnej tylko i wyłącznie za dostarczenie API związanego z obsługą strony TodoMvc. W naszym przykładzie API to jest reprezentowane przez pojedynczą klasę TodoMvc, która zawiera publiczne metody manipulacji interfejsem użytkownika:
1 | public class TodoMvc { |
Ważną cechą nowej klasy jest to, że nie udostępnia swoim klientom (w naszym przypadku klasie testowej) żadnych informacji dotyczących obsługi WebDriver API. Ta obsługa jest realizowana przez obiekt klasy Browser, przekazany za pomocą konstruktora (o samej klasie Browser przeczytasz w dalszej części artykułu).
Jeżeli przyjrzymy się dokładniej metodom klasy TodoMvc to zauważymy, że udostępnia ona metody odzwierciedlające funkcje testowanej strony (page), w tym funkcję nawigacji do strony (metoda navigateTo()). Zatem klasa TodoMvc jest obiektem typu Page Object, gdyż reprezentuje funkcjonalność konkretnej strony, ukrywając jednocześnie szczegóły komunikacji z przeglądarką.
Używanie Page Objects w naszym teście jest bardzo proste: wystarczy zainicjalizować obiekt strony i wywoływać kolejne metody, weryfikując jednocześnie, czy zachowanie strony jest zgodne z oczekiwanym:
1 |
|
Browser Object
Obiekt TodoMvc potrzebuje dostępu do przeglądarki w celu zautomatyzowania poszczególnych funkcji aplikacji. Dostęp ten jest zrealizowany przez obiekty WebDriver API, a klasę, która opakowuje ten dostęp, nazwałem Browser. Klasa Browser dostarcza metody zawarte dotychczas w ogólnej klasie TodoMvcTests (pomiędzy liniami 212 i 251:
1 | public class Browser { |
Klasa Browser zawiera metodę statyczną newBrowser(), która pozwoli na inicjalizację sterownika przeglądarki. Pozostałe metody klasy Browser upraszczają korzystanie z WebDriver API w naszym projekcie. Obiekt klasy Browser inicjalizowany jest w metodzie @BeforeEach i następnie przekazywany do obiektu TodoMvc, który wykorzysta go do automatyzacji kolejnych funkcji aplikacji.
Tip: Jeżeli w trakcie implementowania
Page Objectokazałoby się, że potrzebuje bezpośredniego dostępu doWebDriver API, moglibyśmy stworzyć dodatkową metodę np.public RemoteWebDriver getWebDriver() {}i przy jej pomocy wyeksponować użyty sterownik przeglądarki.
Warto zwrócić uwagę na inną nową metodę, browser.quit(), której nie było we wcześniejszej wersji kodu. Metoda ta odpowiada za zamknięcie sterownika i wykorzystana została w teście:
1 |
|
Struktura pakietów
Po zmianach wprowadzonych powyżej uzyskaliśmy trzy klasy: TodoMvcTests, TodoMvc oraz Browser. Klasy te znajdują się aktualnie w tym samym pakiecie. Warto jednak pomyśleć o wydzieleniu klas do oddzielnych pakietów, tak aby zwiększyć czytelność kodu i ułatwić jego rozbudowę w przyszłości. Pakiety w Javie służą do grupowania podobnych typów klas czy interfejsów, zatem naturalnym będzie utworzenie pakietów dla poszczególnych rodzajów odpowiedzialności:
pl...todomvc.tests- klasy testowe (TodoMvcTests)pl...todomvc.pages- klasyPage Objects(TodoMvc)pl...todomvc.support- klasy wsparcia (Browser)
Diagram klas
Ostateczny diagram klas wygląda następująco:
Warto zauważyć, że zależności między klasami bięgną w jednym kierunku. Fakt, że klasa testowa zależy od klasy Browser, wynika wyłącznie z potrzeby inicjalizacji przeglądarki przed wykonaniem testów oraz zamknięciem jej po wykonaniu testów.
Podsumowanie
Używanie technik programowania obiektowego zwiększa jakość kodu oraz wpływa pozytywnie na utrzymanie i modyfikacje kodu w przyszłości. Sam wzorzeć Page Object nie jest wzorcem skomplikowanym. Wymaga on jednak zrozumienia podstawowych technik projektowania obiektowego, w celu modelowania bardziej skomplikowanych stron i interakcji.
Główne korzyści wynikające z używania tego wzorca to:
- rozdzielenie odpowiedzialności między skryptami testowymi a fukcjonalnością aplikacji,
- możliwość wielokrotnego użycia obiektów,
- łatwość wprowadzania zmian, gdy funkcjonalność aplikacji się zmienia
- zwiększona czytelność kodu
Dalsze kroki
Biblioteka Selenium dostarcza własną implementecję wzorca Page Object. Przeczytasz o niej w części 5 tej serii: JUnit 5 i Selenium - klasa PageFactory z pakietu support biblioteki Selenium
Repozytorium Git projektu
Kod źródłowy opracowany w artykule znajduje się w repozytorium Git: https://gitlab.com/qalabs/blog/junit5-selenium-todomvc-example w branchu 3-page-objects.