W poprzednim artykule pokazaliśmy Wam pierwsze etapy pisania frameworku z niezależnymi testami: konfigurację zależności i tworzenie podstawowych klas. Następny etap pracy to logowanie:
Krok 3: logowanie
Jeśli wszystko poszło zgodnie z planem, powinniśmy mieć uruchomione testy, które generują dużo niepotrzebnego hałasu w konsoli. Dodatkowo, logi nie są zapisywane, co utrudnia proces debugowania.
Aby rozwiązać ten problem, możemy dodać konfigurację logowania i zapisywanie logów do pliku. Użyjemy w tym celu Logback. Należy dodać odpowiednie pliki konfiguracyjne dla Logback (np. logback-test.xml), gdzie zdefiniujemy reguły dotyczące poziomów logowania, docelowych plików logów, itp.
Poniżej znajduje się przykładowa konfiguracja Logbacka, do umieszczenia w resources
To jak konfigurować Logback, to temat na oddzielny artykuł. Powyższą konfigurację skopiujmy do resources, a poniższy plik do package utils.
Krok 4: parametryzacja
Teraz czas na zmodyfikowanie pliku BaseTest, tak by przyjmował parametry z różnych źródeł. Napiszemy kod, który pozwala czytać parametry z pliku XML, jednak można je nadpisać podając JVM Options, a najwyższy priorytet przyjmują parametry wprowadzane z CLI
Zatem <parameter name=browser value=chrome> ma być nadpisywany przez polecenie z CLI – Dbrowser=EDGE.
Zmodyfikujmy więc klasę BaseTest, tak by obsługiwała powyższe założenia:
W 1 kolejności dodajemy 2 pola BrowserType DEAFULT_BROWSER = BrowserType.CHROME i boolean DEFAULT_HEADLESS = TRUE.
Następnie modyfikujemy metodę before, tak by w przypadku podania zbyt małej bądź zbyt dużej liczby argumentów, wybrała wynikający z precedencji, bądź domyślny.
Najpierw potrzebna będzie nam metoda boolean isNullOrBlank(String), która sprawdza, czy argument nie jest null bądź pusty.
W 32 linijce mamy funkcje T resolve(String String T), która odpowiada za precedencje parametrów.
Jak widać ciało metody before zostało zmodyfikowane tak by zwracać właściwy driver na podstawie wyżej wymienionych zmiennych, które definiują instancje Drivera.
Krok 5: Page Object Model
Przestrzegamy branżowych zasad i oczywiście tworzymy framework korzystając z page object model. Po wejściu na stronę qualityminds.com widzimy, że składa się ona z podstron i wspólnego górnego paska nawigacyjnego (TopBar). Podczas naszych testów będziemy musieli otworzyć stronę główną (index) oraz podstronę automatyzacji. Więc będziemy mieli do wykonania 3 PO – HomePagePO, AutoTestPO, TopBarPO – ten ostatni umieścimy jako pole (field) w poprzednich Page Objectach, korzystając z kompozycji.
Dodatkowo, każdy Page Object (ale nie TopBarPO) zawiera metody load() i waitForPageToLoad(), takie, że load() wywołuje waitForPageToLoad(). W przypadku internacjonalizacji, metody te muszą przyjmować I18n jako parametr, by wiedzieć z jakim językiem porównywać widoczne treści.
Te dwa interfejsy zawierają te metody, a interfejs Loadable świetnie pasuje do stron, które wyglądają tak samo w każdym języku lub w przypadku, gdy elementy lokalizacji nie są istotne z punktu widzenia automatycznego testu. Umieśćmy je w pakiecie pages.
Designu samej klasy BasePage nie będę tłumaczył szczegółowo, kod dostępny jest poniżej
Krok 6: lokalizacja
Zanim rozszerzymy klasę BasePage na konkretne podklasy, potrzebujemy narzędzia, które umożliwi nam porównywanie znalezionego tekstu na stronie z oczekiwanym. Możemy oczywiście zdefiniować 18 prywatnych statycznych zmiennych typu String, ale w rzeczywistych frameworkach wykonujemy dużo więcej asercji, a każda zmiana tekstu na stronie wymagałaby modyfikacji kodu. Dlatego znacznie łatwiej i bezpieczniej jest przechowywać kod w zewnętrznym pliku, na przykład w formacie JSON, i odczytywać stamtąd oczekiwane wartości tekstowe.
Określmy, jakie stringi będą nam potrzebne do asercji i zapiszmy je w pliku json:
Teraz napiszemy kod, który pomoże nam czytać z pliku json, potrzebne będą do tego 2 klasy – StringLoader – będzie to singleton ładujący plik json do pamięci oraz Parser – klasa, która plik json zamieni na obiekt JsonNode.
Zacznijmy od klasy Parser, umieśćmy ją w package utils razem z ThreadIdConverter, będzie to prosta implementacja ObjectMapper z Jacksona.
Zawiera ona tylko jedną metodę publiczną, która ma zamienić plik w JsonNode. W argumencie przyjmuje jego lokalizacje w classpath.
Teraz napiszmy klasę StringLoader również w utils.
Klasa jest lazy loaded singletonem, korzystającym z Initialization-on-demand holder idiom.
Konstruktor singletona ładuje plik strings.json do pamięci, a metoda JsonNode strings(I18n) pozwala na otrzymanie obiektu z pliku json zawierającego dane dla konkretnej internacjonalizacji.
Takie rozwiązanie działa, jest jednak bardzo niewygodne w użyciu, za każdym razem, gdy chcielibyśmy np. przywołać konkretny string musielibyśmy pisać:
StringLoader.getInstance().strings(i18n).get(key).asText();
Wobec tego zróbmy interfejs z jedną metodą default, zawierającą właśnie powyższy sparametryzowany kod. Każda klasa, która zaimplementuje ten interfejs nie będzie miała problemów z wyciąganiem stringów z json.
A oto implementacja Interfejsu Localizable (zapiszmy go w package pages):
Zauważmy, że zdefiniowałem już 6 kluczy stringów jako stałe, więc używanie ich nie powinno być problemem. Ten interfejs będzie zaimplementowany przez każdy PO, w którym są elementy, których tekst należy sprawdzić asercją, czyli HomePagePO i AutoTestPO, które zaimplementujemy w następnym korku:
Krok 7: konfiguracja PO
Teraz rozszerzymy klasę BasePage na poszczególne Page Objecty. Ten krok nie zawiera nic skomplikowanego, dlatego też ograniczę się do krótkiego opisu i podania gotowego kodu. Tworząc metody w Page Objectach polecam korzystać z tzw. Fluent API, czyli kazać metodom zwracać this, bądź, gdy metoda powoduje zmianę strony (np. kliknięcie w menu), nową instancję tego Page Objectu, który reprezentuje abstrakcję strony, na którą przechodzimy.
- Zanim jednak ruszymy jakiekolwiek PO, szybko wróćmy do dawno stworzonego enuma, I18n.
Dodajmy stałe abbr, url i href, co ułatwi pisanie metod, które przyjmują I18n jako parametr.
- HomePagePO – implementuje Loadable
, LocaleLoadable oraz Localizable: Metody load() i waitForPageToLoad() (wywoływana przez load()) są przydatne do otwierania strony bez określania języka – w takim przypadku strona jest ładowana domyślnie. Mamy również te same dwie metody z parametrem typu I18n, które używamy, gdy zmieniamy język, ponieważ wtedy ładowany jest inny adres, a metoda waitForPageToLoad(I18n) sprawdza, czy strona została poprawnie załadowana w różnych językach, zależnie od parametru I18n.
Łatwo zauważyć, że metoda waitForPageToLoad() korzysta z metody default z interfejsu Localizable, aby porównywać tekst ze strony z oczekiwanym tekstem z pliku json.
Dodatkowo jako pole klasy, mamy instancję TopBarPO, która umożliwia dostęp do górnego paska w dowolnym momencie korzystania z HomePagePO, co wykorzystamy w testach. - AutoTestPO – implementuje LocaleLoadable
oraz Localizable: Podobnie jak w HomePagePO, komponentem klasy jest również TopBarPO. - TopBarPO – jedyny PO, który nie jest abstrakcją nad danym widokiem strony, a komponentem widocznym z każdego widoku, dlatego też jest wkomponowany w poprzednie PO.
Krok 8: testowanie
Mamy już wszystkie metody niezbędne do nawigowania po stronie, jedyne co nam pozostało, to wywołać je i mamy gotowy test automatyczny.
W package test zróbmy klasę LocalisationTest z metodą void verifyTestAutomationPageTranslation(I18n), ponieważ taki sam test musi być wykonany trzykrotnie dla każdego z języków. W tym celu skorzystamy z DataProvidera, któremu każmy zwrócić I18n[] czyli I18n.values(). Ustawmy adnotacje @DataProvider(parallel = true), dzięki czemu, póki starczy wątków, metody mogą być uruchamiane równolegle.
Sam test jest bardzo prosty, dzięki fluent API raz wywołujemy new HomePagePO(driver()), a potem już do końca testu chainujemy metody w tej kolejności (poniżej opis krok po kroku). Dzięki użyciu DataProvidera, wykonają się one 3 razy, za każdym razem z innym parametrem i18n.
- Załaduj stronę główną
- Zamknij popup z Cookies.
- Zwróć instancje TopBarPO
- Sprawdź, że obecny język to angielski
- Najedź myszą na menu języka
- Zmień język na i18n (ta metoda nie robi nic, jeśli i18n == I18n.ENGLISH).
- Sprawdź czy obecna ikonka języka odpowiada i18n
- Najedź myszą na menu Portfolio
- Kliknij na podmenu automation tests
- Poczekaj, aż strona się załaduje w języku i18n
- Sprawdź przycisk contact us dla języka i18n
I tyle.
Zmodyfikujmy jeszcze plik testng.xml
I uruchamiamy testy – wszystko powinno działać.
Jak zwykle zachęcam do pobawienia się plikiem konfiguracyjnym, dopisania własnych testów (które chociażby tylko miały pisać wiadomość na konsoli) i posprawdzania różnych wariantów paralelizacji.
Czytelniczka lub czytelnik na pewno zauważą, że głębokość, na której chcemy dokonywać paralelizacji, wymaga od nas coraz większej dyscypliny w budowaniu frameworku. Uruchamianie testów na poziomie poszczególnych metod wymaga największej dyscypliny w pisaniu kodu.
Na stronie repozytorium, na gałęzi main jest jeszcze dostępny workflow dla GitHuba oraz plik bat uruchamiający skrypt PowerShell, który natychmiast wyłącza wszystkie sesje Chrome, Edge, Firefoxa i ich driverów – przydatne, gdy podczas debugowania zostaje nam mnóstwo niedomkniętych okienek.
0 komentarzy