Automatyzacja w Selenium: Page Object Model i Page Factory

Opublikowany: 2022-03-11

Pisanie testów automatycznych to coś więcej niż luksus dla każdego zespołu programistów zwinnych. Jest to potrzeba i jest niezbędnym narzędziem do szybkiego znajdowania błędów we wczesnych fazach cykli tworzenia oprogramowania. Gdy pojawia się nowa funkcja, która jest nadal w fazie rozwoju, programiści mogą przeprowadzać automatyczne testy i sprawdzać, jak te zmiany wpływają na inne części systemu. W tym artykule wyjaśnimy, jak przyspieszyć automatyczne testowanie przy użyciu modelu Page Object w Selenium.

Dzięki automatyzacji testów możliwe jest obniżenie kosztów naprawiania błędów i ogólna poprawa procesu zapewniania jakości oprogramowania (QA). Dzięki odpowiednim testom programiści mają szansę na znalezienie i naprawienie błędów, jeszcze zanim dotrą do QA. Automatyzacja testów dodatkowo pomaga nam zautomatyzować przypadki testowe i funkcje, które stale się cofają. W ten sposób QA mają więcej czasu na testowanie innych części aplikacji. Ponadto pomaga to w zapewnieniu jakości produktu w wydaniach produkcyjnych. W rezultacie otrzymujemy produkty, które są efektywniej bardziej stabilne, a proces zapewniania jakości jest bardziej wydajny.

Automatyzacja Selenium upraszcza automatyzację testów aplikacji internetowych

Selenium upraszcza automatyzację testów aplikacji internetowych
Ćwierkać

Chociaż pisanie testów automatycznych może wydawać się łatwym zadaniem dla programistów i inżynierów, wciąż istnieje możliwość uzyskania słabo zaimplementowanych testów i wysokich kosztów utrzymania kodu w dowolnym procesie zwinnym. Próba ciągłego dostarczania zmian lub funkcji w dowolnym projekcie programistycznym zwinnym może okazać się kosztowna, gdy zaangażowane są testy. Zmiana jednego elementu na stronie internetowej, na którym opiera się 20 testów, będzie wymagała przejrzenia tych 20 procedur testowych i zaktualizowania każdego z nich, aby dostosować się do nowo wprowadzonej zmiany. Może to być nie tylko bardzo czasochłonne, ale także poważny czynnik demotywujący, jeśli chodzi o wdrażanie testów automatycznych na wczesnym etapie.

Ale co by było, gdybyśmy mogli dokonać zmiany tylko w jednym miejscu i korzystać z niej we wszystkich odpowiednich procedurach testowych? W tym artykule przyjrzymy się automatycznym testom w Selenium oraz temu, jak możemy używać modeli Page Object do pisania procedur testowych, które można konserwować i ponownie wykorzystywać.

Model obiektowy strony w Selenium

Model obiektu strony to wzorzec projektowy obiektu w Selenium, w którym strony internetowe są reprezentowane jako klasy, a różne elementy na stronie są zdefiniowane jako zmienne w klasie. Wszystkie możliwe interakcje użytkownika można następnie zaimplementować jako metody w klasie:

 clickLoginButton(); setCredentials(user_name,user_password);

Ponieważ dobrze nazwane metody w klasach są łatwe do odczytania, działa to jako elegancki sposób na zaimplementowanie procedur testowych, które są zarówno czytelne, jak i łatwiejsze do utrzymania lub aktualizacji w przyszłości. Na przykład:

Do obsługi modelu Page Object wykorzystujemy Page Factory. Page Factory w Selenium jest rozszerzeniem Page Object i może być używany na różne sposoby. W tym przypadku użyjemy Page Factory do zainicjowania elementów internetowych, które są zdefiniowane w klasach stron internetowych lub obiektach strony.

Klasy stron sieci Web lub obiekty strony zawierające elementy sieci Web muszą zostać zainicjowane przy użyciu narzędzia Page Factory, zanim będzie można użyć zmiennych elementów sieci Web. Można to zrobić po prostu za pomocą funkcji initElements w PageFactory:

 LoginPage page = new LoginPage(driver); PageFactory.initElements(driver, page);

Lub jeszcze prościej:

 LoginPage page = PageFactory.intElements(driver,LoginPage.class)

Lub w konstruktorze klasy strony internetowej:

 public LoginPage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); }

Page Factory zainicjuje każdą zmienną WebElement z odwołaniem do odpowiedniego elementu na rzeczywistej stronie internetowej na podstawie skonfigurowanych „lokatorów”. Odbywa się to za pomocą adnotacji @FindBy . Dzięki tej adnotacji możemy zdefiniować strategię wyszukiwania elementu wraz z informacjami niezbędnymi do jego identyfikacji:

 @FindBy(how=How.NAME, using="username") private WebElement user_name;

Za każdym razem, gdy metoda jest wywoływana na tej zmiennej WebElement , sterownik najpierw znajdzie ją na bieżącej stronie, a następnie zasymuluje interakcję. W przypadku, gdy pracujemy na prostej stronie, wiemy, że za każdym razem, gdy go szukamy, znajdziemy element na stronie, a także wiemy, że w końcu odejdziemy z tej strony i nie wrócimy do niej, możemy buforować wyszukane pole za pomocą innej prostej adnotacji:

 @FindBy(how=How.NAME, using="username") @CacheLookup private WebElement user_name;

Całą definicję zmiennej WebElement można zastąpić jej znacznie bardziej zwięzłą formą:

 @FindBy(name="username") private WebElement user_name;

Adnotacja @FindBy obsługuje kilka innych strategii, które nieco ułatwiają:

 id, name, className, css, tagName, linkText, partialLinkText, xpath
 @FindBy() private WebElement user_name; @FindBy(name="passsword") private WebElement user_password; @FindBy(className="h3") private WebElement label; @FindBy(css=”#content”) private WebElement text;

Po zainicjowaniu te zmienne WebElement mogą być następnie używane do interakcji z odpowiednimi elementami na stronie. Poniższy kod będzie na przykład:

 user_password.sendKeys(password);

… wyślij podaną sekwencję naciśnięć klawiszy do pola hasła na stronie i jest to równoznaczne z:

 driver.findElement(By.name(“user_password”)).sendKeys(password);

Idąc dalej, często natkniesz się na sytuacje, w których musisz znaleźć listę elementów na stronie, a wtedy przydaje się @FindBys :

 @FindBys(@FindBy(css=”div[class='yt-lockup-tile yt-lockup-video']”))) private List<WebElement> videoElements;

Powyższy kod znajdzie wszystkie elementy div mające dwie nazwy klas „yt-lockup-tile” i „yt-lockup-video”. Możemy to jeszcze bardziej uprościć, zastępując go następującym:

 @FindBy(how=How.CSS,using="div[class='yt-lockup-tile yt-lockup-video']") private List<WebElement> videoElements;

Dodatkowo możesz użyć @FindAll z wieloma adnotacjami @FindBy , aby wyszukać elementy pasujące do dowolnego z podanych lokalizatorów:

 @FindAll({@FindBy(how=How.ID, using=”username”), @FindBy(className=”username-field”)}) private WebElement user_name;

Teraz, gdy możemy reprezentować strony internetowe jako klasy Java i używać Page Factory do łatwego inicjowania zmiennych WebElement , nadszedł czas, aby zobaczyć, jak możemy napisać proste testy Selenium przy użyciu wzorca Page Object i Page Factory.

Prosty projekt automatyzacji testów Selenium w Javie

W naszym samouczku dotyczącym modelu Page Object zautomatyzujmy rejestrację programisty w Toptal. Aby to zrobić, musimy zautomatyzować następujące kroki:

  • Odwiedź www.toptal.com

  • Kliknij przycisk „Zastosuj jako programista”

  • Na stronie portalu najpierw sprawdź, czy jest otwarta

  • Kliknij przycisk „Dołącz do Toptal”

  • Wypełnij formularz

  • Prześlij formularz, klikając przycisk „Dołącz do Toptal”

Konfiguracja projektu

  • Pobierz i zainstaluj Java JDK

  • Pobierz i zainstaluj InteliJ Idea

  • Utwórz nowy projekt Maven

  • Połącz „Project SDK” z JDK, np.: w systemie Windows „C:\Program Files\Java\jdkxxx”

  • Skonfiguruj groupId i artefaktId:

 <groupId>SeleniumTEST</groupId> <artifactId>Test</artifactId>
  • Dodaj zależności Selenium i JUnit Maven w pliku POM projektu
 <dependencies> <!-- JUnit --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>${junit.version}</version> <scope>test</scope> </dependency> <!-- Selenium --> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-firefox-driver</artifactId> <version>${selenium.version}</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-support</artifactId> <version>${selenium.version}</version> </dependency> <dependency> <groupId>org.seleniumhq.selenium</groupId> <artifactId>selenium-java</artifactId> <version>${selenium.version}</version> </dependency> </dependencies>

Zastąp wersję Selenium i wersję JUnit najnowszymi numerami wersji, które można znaleźć, wyszukując JUnit Maven w Google i witrynie Selenium.

W tym momencie, jeśli automatyczne kompilowanie jest włączone, zależności powinny rozpocząć się automatycznie. Jeśli nie, po prostu aktywuj wtyczki> zainstaluj> zainstaluj: zainstaluj w panelu Maven Projects po prawej stronie IntelliJ Idea IDE.

Samouczek testowania selenu Zrzut ekranu IDE

Gdy projekt zostanie załadowany, możemy rozpocząć tworzenie naszego pakietu testowego w „src/test/java”. Nazwij pakiet „com.toptal” i utwórz pod nim dwa dodatkowe pakiety: „com.toptal.webpages” i „com.toptal.tests”.

zrzut ekranu samouczka testowania selenu

Będziemy przechowywać nasze klasy Page Object/Page Factory w „com.toptal.webpages”, a procedury testowe w „com.toptal.tests”.

Teraz możemy zacząć tworzyć nasze klasy Page Object.

Obiekt strony głównej

Pierwszym, który musimy wdrożyć, jest strona główna Toptal (www.toptal.com). Utwórz klasę pod „com.toptal.webpages” i nazwij ją „HomePage”.

 package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.How; import org.openqa.selenium.support.PageFactory; public class HomePage { private WebDriver driver; //Page URL private static String PAGE_URL="https://www.toptal.com"; //Locators //Apply as Developer Button @FindBy(how = How.LINK_TEXT, using = "APPLY AS A DEVELOPER") private WebElement developerApplyButton; //Constructor public HomePage(WebDriver driver){ this.driver=driver; driver.get(PAGE_URL); //Initialise Elements PageFactory.initElements(driver, this); } public void clickOnDeveloperApplyButton(){ developerApplyButton.click(); } }

Określanie lokalizatorów elementów

Na stronie głównej Toptala interesuje nas w szczególności jeden element, a jest nim przycisk „Aplikuj jako programista”. Możemy znaleźć ten element, dopasowując tekst, co robimy powyżej. Podczas modelowania stron internetowych jako klas Page Object, znajdowanie i identyfikowanie elementów może często stać się uciążliwe. Dzięki narzędziom do debugowania Google Chrome lub Firefox można to ułatwić. Klikając prawym przyciskiem myszy dowolny element na stronie, możesz aktywować opcję „Sprawdź element” z menu kontekstowego, aby uzyskać szczegółowe informacje o elemencie.

Jednym z popularnych (i preferowanym przeze mnie) sposobów jest znajdowanie elementów za pomocą rozszerzenia FireBug Firefoksa w połączeniu ze sterownikiem sieciowym Firefox w Selenium. Po zainstalowaniu i włączeniu rozszerzenia FireBug możesz kliknąć prawym przyciskiem myszy na stronie i wybrać „Sprawdź element za pomocą FireBug”, aby otworzyć FireBug. Z zakładki HTML FireBug możesz skopiować XPath, ścieżkę CSS, nazwę tagu lub „identyfikator” (jeśli jest dostępny) dowolnego elementu na stronie.

model obiektu strony w selenu: określanie lokalizatorów elementów

Kopiując XPath elementu na powyższym zrzucie ekranu, możemy utworzyć dla niego pole WebElement w naszym obiekcie strony w następujący sposób:

 @FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1") WebElement heading;

Lub, aby uprościć sprawę, możemy tutaj użyć nazwy tagu „h1”, o ile jednoznacznie identyfikuje ona interesujący nas element:

 @FindBy(tagName = "h1") WebElement heading;

Obiekt strony DeveloperPortalPage

Następnie potrzebujemy obiektu Page, który reprezentuje stronę portalu dla programistów, do którego możemy dotrzeć, klikając przycisk „Zastosuj jako programista”.

Na tej stronie mamy dwa interesujące elementy. Aby ustalić, czy strona została załadowana, chcemy zweryfikować istnienie nagłówka. Potrzebujemy również pola WebElement dla przycisku „Dołącz do Toptal”.

 package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class DeveloperPortalPage { private WebDriver driver; @FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1") private WebElement heading; @FindBy(linkText = "JOIN TOPTAL") private WebElement joinToptalButton; //Constructor public DeveloperPortalPage (WebDriver driver){ this.driver=driver; //Initialise Elements PageFactory.initElements(driver, this); } //We will use this boolean for assertion. To check if page is opened public boolean isPageOpened(){ return heading.getText().toString().contains("Developer portal"); } public void clikOnJoin(){ joinToptalButton.click(); } }

Obiekt strony DeveloperApplyPage

I na koniec, dla naszego trzeciego i ostatniego obiektu strony dla tego projektu, definiujemy taki, który reprezentuje stronę zawierającą formularz aplikacji dla programistów. Ponieważ mamy do czynienia z wieloma polami formularza, definiujemy jedną zmienną WebElement dla każdego pola formularza. Odnajdujemy każde pole według ich „id” i definiujemy specjalne metody ustawiające dla każdego pola, które symulują naciśnięcia klawiszy dla odpowiednich pól.

 package com.toptal.webpages; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.FindBy; import org.openqa.selenium.support.PageFactory; public class DeveloperApplyPage { private WebDriver driver; @FindBy(tagName = "h1") WebElement heading; @FindBy() WebElement developer_email; @FindBy() WebElement developer_password; @FindBy() WebElement developer_password_confirmation; @FindBy() WebElement developer_full_name; @FindBy() WebElement developer_skype; @FindBy() WebElement join_toptal_button; //Constructor public DeveloperApplyPage(WebDriver driver){ this.driver=driver; //Initialise Elements PageFactory.initElements(driver, this); } public void setDeveloper_email(String email){ developer_email.clear(); developer_email.sendKeys(email); } public void setDeveloper_password(String password){ developer_password.clear(); developer_password.sendKeys(password); } public void setDeveloper_password_confirmation(String password_confirmation){ developer_password_confirmation.clear(); developer_password_confirmation.sendKeys(password_confirmation); } public void setDeveloper_full_name (String fullname){ developer_full_name.clear(); developer_full_name.sendKeys(fullname); } public void setDeveloper_skype (String skype){ developer_skype.clear(); developer_skype.sendKeys(skype); } public void clickOnJoin(){ join_toptal_button.click(); } public boolean isPageOpened(){ //Assertion return heading.getText().toString().contains("Apply to join our network as a developer"); } }

Pisanie prostego testu na selen

Dzięki klasom Page Object reprezentującym nasze strony i interakcjom użytkownika jako ich metodom, możemy teraz napisać naszą prostą procedurę testową jako serię prostych wywołań metod i asercji.

 package com.toptal.tests; import com.toptal.webpages.DeveloperApplyPage; import com.toptal.webpages.DeveloperPortalPage; import com.toptal.webpages.HomePage; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import java.net.URL; import java.util.concurrent.TimeUnit; public class ApplyAsDeveloperTest { WebDriver driver; @Before public void setup(){ //use FF Driver driver = new FirefoxDriver(); driver.manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS); } @Test public void applyAsDeveloper() { //Create object of HomePage Class HomePage home = new HomePage(driver); home.clickOnDeveloperApplyButton(); //Create object of DeveloperPortalPage DeveloperPortalPage devportal= new DeveloperPortalPage(driver); //Check if page is opened Assert.assertTrue(devportal.isPageOpened()); //Click on Join Toptal devportal.clikOnJoin(); //Create object of DeveloperApplyPage DeveloperApplyPage applyPage =new DeveloperApplyPage(driver); //Check if page is opened Assert.assertTrue(applyPage.isPageOpened()); //Fill up data applyPage.setDeveloper_email("[email protected]"); applyPage.setDeveloper_full_name("Dejan Zivanovic Automated Test"); applyPage.setDeveloper_password("password123"); applyPage.setDeveloper_password_confirmation("password123"); applyPage.setDeveloper_skype("automated_test_skype"); //Click on join //applyPage.clickOnJoin(); } @After public void close(){ driver.close(); } }

Uruchamianie testu

W tym momencie struktura twojego projektu powinna wyglądać tak:

przykład badania selenu

Jeśli chcesz uruchomić test, wybierz „ApplyAsDeveloperTest” z drzewa, kliknij go prawym przyciskiem myszy, a następnie wybierz Uruchom „ApplyAsDeveloperTest” .

przykład badania selenu

Po uruchomieniu testu możesz zobaczyć wyniki w lewym dolnym rogu swojego IDE:

przykład badania selenu

Wniosek

Page Object i Page Factory ułatwiają modelowanie stron internetowych w Selenium i automatyczne ich testowanie oraz znacznie ułatwiają życie zarówno programistom, jak i QA. Po prawidłowym wykonaniu te klasy Page Object mogą być ponownie użyte w całym zestawie testów i dać sobie możliwość wczesnej implementacji automatycznych testów Selenium dla Twoich projektów, bez narażania na szwank zwinnego programowania. Oddzielając interakcje użytkownika w modelach obiektów strony i utrzymując proste i proste procedury testowe, możesz przy niewielkim wysiłku dostosować zestaw testów do zmieniających się wymagań.

Mam nadzieję, że udało mi się pokazać, jak pisać ładny i czysty kod testowy, który jest łatwy w utrzymaniu. Artykuł zakończę moim ulubionym cytatem z QA:

Pomyśl dwa razy, kod raz!

Powiązane: Skrobanie stron internetowych za pomocą przeglądarki Headless: samouczek lalkarza