Framework testów w Selenium z paralelizacją, obsługą wielu przeglądarek i internacjonalizacją

Aktualności | Artykuły | Blog | QualityMinds | Testing

W poniższym artykule postaram się przybliżyć odpowiedź na trzy pytania, które często pojawiają się podczas pisania testów:

  • Jak napisać framework z całkowicie niezależnymi testami, które można uruchamiać równolegle w dowolnej konfiguracji (a nawet zagnieżdżać paralelizację)?
  • Jak dodać wsparcie dla różnych przeglądarek?
  • Jak dodać wsparcie dla różnych wersji językowych (lokalizacji)?

Cały kod znajduje się w tym repozytorium i podzielony jest na 8 kroków. Zakładam, że czytelniczka lub czytelnik zna Javę na poziomie juniora, oraz ma pojęcie o automatyzacji testów i Selenium, ponieważ, aby zachować przejrzystość, nie będę tłumaczył każdej rzeczy od podstaw, a skupię się na tytułowych zagadnieniach.

Załóżmy, że dostaliśmy zadanie przetestowania strony internetowej naszego pracodawcy. Aby uniknąć monotonnej pracy, zautomatyzujemy to zadanie. Strona jest dostępna w trzech językach – angielskim, niemieckim i polskim i najczęściej otwierana jest na trzech przeglądarkach – Chrome, Edge i Firefox. Byłoby świetnie, gdybyśmy mogli codziennie rano uruchamiać automatyzację za pomocą cron-a i po przyjściu do pracy wiedzieć, co działa, a co nie. W tym celu musimy dodać obsługę plików konfiguracyjnych lub interfejsu wiersza poleceń (CLI). Przydałby się również tryb bezinterfejsowy (headless), bo zasoby są kosztowne. Kod napiszemy w Javie z wykorzystaniem biblioteki Selenium, bo tak lubi autor. Nasz pracodawca chce mieć pewność, że przycisk „Kontakt” w podmenu „Automatyzacja Testów” działa poprawnie. Musimy także sprawdzić, czy wersja polska przekierowuje na polskie adresy e-mail, a wersje angielska i niemiecka na odpowiednie adresy w tych językach.

Podsumowując, musimy stworzyć framework, który obsłuży przeglądarki Chrome, Edge i Firefox oraz będzie można go uruchomić na platformie GitHub Actions (lub innym środowisku wykonawczym). Testy będą niezależne od siebie i możliwe będzie ich równoczesne uruchamianie na wielu maszynach, w zależności od naszych możliwości. W przyszłości rozważam dodanie rozszerzeń lub ulepszeń do tego kursu, które obejmą obsługę zmiennych środowiskowych, Dockera, Selenium Grid oraz innych interesujących funkcji. Do napisania kodu wykorzystałem IntelliJ Ultimate. Cały kod jest dostępny na platformie GitHub, a proces tworzenia kodu został podzielony na kolejne gałęzie, zaczynając od 001 i kończąc na 008.

Stwórzmy zatem nowy projekt w IntelliJ.

Zacznijmy od utworzenia nowego projektu w IntelliJ. Ja będę używał Gradle i języka Kotlin jako DSL, ponieważ nie przepadam za Groovym. Po kliknięciu przycisku Create, IntelliJ utworzy dla nas szkielet projektu. Przy okazji warto wspomnieć, że mam nawyk umieszczania wszystkich testów automatycznych i kodu pomocniczego w sekcji test projektu (a nie w main). Właściwie nie ma to znaczenia, gdzie trzymamy te testy, ponieważ nie będziemy budować tego projektu ani tworzyć żadnych archiwów. Musimy tylko pamiętać, żeby oznaczyć wszystkie zależności jako „test” i propagować JVM options do zadania „test”.

Krok 1: konfiguracja zależności

Zajmijmy się teraz plikiem build.gradle.kts (nie będziemy go więcej zmieniać, dlatego już teraz wrzucimy wszystkie potrzebne zmiany). Poniżej wyjaśnię kilka interesujących linijek.



Jeśli chodzi o zależności, to użyjemy Selenium i TestNG oraz WebDriverManager do konfigurowania Drivera. AssertJ do asercji, Logback i SLF4J do logowania i Jackson do czytania plików json (w którym będą stringi w 3 językach, z którymi porównamy treść na stronie). Do całości dodamy Lombok dla wygody.

Linijki 42 – 57, to konfiguracja testów, w linijce 44 piszemy, gdzie TestNG ma szukać pliku konfiguracyjnego – w folderze testng. Zostawiamy sobie możliwość zmodyfikowania nazwy pliku, korzystając z properties – suiteFile – które domyślnie ma mieć wartość testng.xml.

Linijka 47 jest potrzeba, aby JVM options były widoczne także dla tasków test, inaczej nie dałoby się przekazywać parametrów korzystając z CLI.

Krok 2: podstawowe klasy

Za nami konfigurowanie zależności. Teraz zajmiemy się uruchomieniem pierwszego testu i dodaniem niezbędnych klas, o które oparty będzie framework.

Zacznijmy od stworzenia package base, a w nim klasy AutomationException – wrappera dla RuntimeExpection oraz enumów BrowserType z wartościami CHROME, FIREFOX i EDGE oraz I18n z ENGLISH, GERMAN, POLISH.

Następnie skonfigurujmy WebDriverManager, aby mieć z głowy samodzielne zarządzanie driverem. Logikę umieśćmy w package wdm. Zacznijmy od klasy DriverManagerFactory z jedną metodą DriverManager getManager()



Ponieważ będziemy korzystać z trzech różnych przeglądarek, potrzebujemy trzy implementacje naszego interfejsu, tutaj pokażę implementacje dla Chrome, kod dla innych przeglądarek dostępny jest w repozytorium na GitHubie:



A poniżej kod interfejsu DriverManager:



Teraz nadchodzi chyba najważniejszy moment w tworzeniu całego frameworka:

Po zaimplementowaniu trzech interfejsów, mamy działający WebDriverManager. Teraz stwórzmy klasę BaseTest, która będzie klasą bazową dla wszystkich klas testów. Przy projektowaniu tej klasy musimy się chwilę zastanowić nad jej strukturą. Ponieważ chcemy, aby nasz framework był w stanie uruchamiać każdą metodę testową równolegle, musimy zadbać o to, aby nie były one od siebie w żaden sposób zależne i aby stan żadnego testu nie zależał od wyniku poprzedniego. Ponadto, przed każdym testem powinniśmy tworzyć nową instancję drivera, a po każdym teście go wyłączać. Dzięki temu ograniczymy głównie problemy techniczne związane z równoległym wykonywaniem testów. W związku z tym, w klasie BaseTest skorzystamy z adnotacji @BeforeMethod i @AfterMethod do tworzenia i niszczenia instancji drivera.

Drugim istotnym aspektem jest możliwość parametryzacji testów. Musimy w jakiś sposób przekazać naszemu narzędziu informacje o przeglądarce, na której chcemy uruchamiać testy oraz czy ma być używany tryb headless czy nie. Domyślnie korzystamy z pliku suite file, którego lokalizację i nazwę ustaliliśmy jako testng/testng.xml w kroku 001. Dodatkowo, byłoby dobrze, gdybyśmy mogli nadpisywać te parametry, podając je za pomocą CLI lub jako JVM options w konfiguracji IntelliJ. Na razie skupmy się jednak na dostarczeniu parametrów z pliku XML. W kolejnych krokach dodamy pozostałe funkcjonalności.

Aby dostarczyć parametry z pliku XML, możemy użyć mechanizmu TestNG, który umożliwia przekazywanie parametrów z plików XML bezpośrednio do metod testowych. Możemy zdefiniować te parametry w pliku suite file (testng.xml) i odczytać je w naszych testach, o czym za chwilę. Na razie stwórzmy klasę BaseTest, która będzie mogła odczytać te parametry:



W linii 6 deklarujemy pole `ThreadLocal driver`, które jest niezbędne do prawidłowej paralelizacji.

W linii 24 deklarujemy metodę `before`, która pobiera dwa opcjonalne parametry: `browser` i `headless`, zgodnie z konfiguracją. Adnotacja z linii 22 (`@Parameters`) odpowiada za przekazywanie tych parametrów, a adnotacja `@Optional` w sygnaturze metody sprawia, że jeśli nie zostaną dostarczone argumenty, framework będzie nadal działał – musimy tylko ustawić wartości domyślne, o czym dowiemy się w kolejnych krokach.

W linii 25 inicjalizujemy driver zgodnie z ustawieniami odczytanymi z pliku XML.



Następnie tworzymy plik testng.xml w katalogu testng oraz package tests z klasa ExampleTest dziedziczącą z BaseTest, w której zrobimy jedną metodę exampleTest otwierającą naszą stronę – driver().get(„https://qualityminds.com”;

Plik XML jest skonfigurowany tak, by uruchomić naszą metodę exampleTest raz na 2 różnych przeglądarkach. Klikamy prawym na plik testng.xml i wciskamy run, bądź też wpisujemy w terminal: ./gradlew clean test.

Zachęcam do dopisania przykładowych testów do klasy ExampleTest, stworzenia dodatkowej klasy z testami oraz eksperymentowania z liczbą thread-count oraz parametrem parallel=”methods” (dostępne to tests, classes, methods, instances, wartości true i false są deprecated).

Jeśli z jakiegoś powodu otrzymujemy błąd przy próbie uruchomienia pliku XML, możemy postępować według następujących kroków:

Klikamy na przycisk “Edit Configuration” obok przycisku “Play” na górnym panelu IntelliJ.

W oknie konfiguracji szukamy opcji “Use classpath or module“.

Wybieramy opcję “seleniumframework.test” (jeśli nazwaliśmy projekt “seleniumframework“).

Dzięki temu ustawieniu będziemy korzystać z odpowiednich ścieżek i modułów podczas uruchamiania testów, co powinno rozwiązać problem z błędem związanym z plikiem XML.

W następnym artykule przybliżę Wam następne kroki, zaczynając od konfiguracji logowania. Zapraszam Was do lektury!

0 komentarzy

Napisane przez

Rafał Michalski