Automazione in Selenium: Page Object Model e Page Factory
Pubblicato: 2022-03-11Scrivere test automatizzati è più di un semplice lusso per qualsiasi team di sviluppo software agile. È un'esigenza ed è uno strumento essenziale per trovare rapidamente i bug durante le prime fasi dei cicli di sviluppo del software. Quando c'è una nuova funzionalità che è ancora in fase di sviluppo, gli sviluppatori possono eseguire test automatizzati e vedere in che modo altre parti del sistema sono interessate da tali modifiche. Questo articolo spiegherà come accelerare i test automatizzati utilizzando il modello Page Object in Selenium.
Attraverso l'automazione dei test, è possibile ridurre i costi di correzione dei bug e apportare un miglioramento generale al processo di garanzia della qualità del software (QA). Con test adeguati, gli sviluppatori hanno la possibilità di trovare e risolvere i bug anche prima di arrivare al QA. L'automazione dei test ci aiuta ulteriormente ad automatizzare i casi di test e le funzionalità che sono in costante regresso. In questo modo i QA hanno più tempo per testare altre parti dell'applicazione. Inoltre, questo aiuta a garantire la qualità del prodotto nelle versioni di produzione. Di conseguenza, otteniamo prodotti effettivamente più stabili e un processo di controllo qualità più efficiente.
Sebbene la scrittura di test automatizzati possa sembrare un compito facile per sviluppatori e ingegneri, c'è ancora la possibilità di finire con test mal implementati e l'alto costo della manutenzione del codice in qualsiasi processo agile. Cercare di fornire costantemente modifiche o funzionalità in qualsiasi progetto di sviluppo agile può rivelarsi costoso quando sono coinvolti i test. La modifica di un elemento su una pagina Web su cui si basano 20 test richiederà di eseguire queste 20 routine di test e aggiornarle per adattarsi a questo cambiamento appena introdotto. Non solo questo può richiedere molto tempo, ma può anche essere un serio fattore demotivante quando si tratta di implementare test automatizzati all'inizio.
Ma cosa accadrebbe se potessimo apportare la modifica in un solo posto e farla utilizzare a tutte le routine di test pertinenti? In questo articolo, daremo un'occhiata ai test automatizzati in Selenium e come possiamo utilizzare i modelli Page Object per scrivere routine di test gestibili e riutilizzabili.
Modello a oggetti di pagina in selenio
Il modello a oggetti di pagina è un modello di progettazione di oggetti in Selenium, in cui le pagine Web sono rappresentate come classi e i vari elementi nella pagina sono definiti come variabili nella classe. Tutte le possibili interazioni dell'utente possono quindi essere implementate come metodi sulla classe:
clickLoginButton(); setCredentials(user_name,user_password);
Poiché i metodi ben definiti nelle classi sono facili da leggere, questo funziona come un modo elegante per implementare routine di test che sono sia leggibili che più facili da mantenere o aggiornare in futuro. Per esempio:
Per supportare il modello Page Object, utilizziamo Page Factory. Page Factory in Selenium è un'estensione di Page Object e può essere utilizzata in vari modi. In questo caso useremo Page Factory per inizializzare gli elementi web che sono definiti nelle classi di pagine web o Page Objects.
Le classi di pagine Web o gli oggetti Page contenenti elementi Web devono essere inizializzati utilizzando Page Factory prima di poter utilizzare le variabili degli elementi Web. Questo può essere fatto semplicemente attraverso l'uso della funzione initElements su PageFactory:
LoginPage page = new LoginPage(driver); PageFactory.initElements(driver, page);
Oppure, ancora più semplice:
LoginPage page = PageFactory.intElements(driver,LoginPage.class)
Oppure, all'interno del costruttore di classi della pagina web:
public LoginPage(WebDriver driver) { this.driver = driver; PageFactory.initElements(driver, this); }
Page Factory inizializzerà ogni variabile WebElement con un riferimento a un elemento corrispondente sulla pagina Web effettiva in base ai "localizzatori" configurati. Questo viene fatto attraverso l'uso delle annotazioni @FindBy . Con questa annotazione possiamo definire una strategia per la ricerca dell'elemento, insieme alle informazioni necessarie per identificarlo:
@FindBy(how=How.NAME, using="username") private WebElement user_name;
Ogni volta che un metodo viene chiamato su questa variabile WebElement , il driver lo troverà prima nella pagina corrente e quindi simulerà l'interazione. Nel caso in cui stiamo lavorando con una pagina semplice, sappiamo che troveremo l'elemento sulla pagina ogni volta che lo cercheremo, e sappiamo anche che alla fine ci allontaneremo da questa pagina e non ci torneremo, possiamo memorizzare nella cache il campo cercato utilizzando un'altra semplice annotazione:
@FindBy(how=How.NAME, using="username") @CacheLookup private WebElement user_name;
L'intera definizione della variabile WebElement può essere sostituita con la sua forma molto più concisa:
@FindBy(name="username") private WebElement user_name;
L'annotazione @FindBy supporta una manciata di altre strategie che rendono le cose un po' più semplici:
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;
Una volta inizializzate, queste variabili WebElement possono quindi essere utilizzate per interagire con gli elementi corrispondenti nella pagina. Il codice seguente, ad esempio:
user_password.sendKeys(password);
… invia la sequenza di battitura data al campo password della pagina, ed è equivalente a:
driver.findElement(By.name(“user_password”)).sendKeys(password);
Andando avanti, ti imbatterai spesso in situazioni in cui devi trovare un elenco di elementi su una pagina, ed è allora che @FindBys torna utile:
@FindBys(@FindBy(css=”div[class='yt-lockup-tile yt-lockup-video']”))) private List<WebElement> videoElements;
Il codice sopra troverà tutti gli elementi div con due nomi di classe "yt-lockup-tile" e "yt-lockup-video". Possiamo semplificarlo ancora di più sostituendolo con il seguente:
@FindBy(how=How.CSS,using="div[class='yt-lockup-tile yt-lockup-video']") private List<WebElement> videoElements;
Inoltre, puoi utilizzare @FindAll con più annotazioni @FindBy per cercare elementi che corrispondono a uno qualsiasi dei localizzatori indicati:
@FindAll({@FindBy(how=How.ID, using=”username”), @FindBy(className=”username-field”)}) private WebElement user_name;
Ora che possiamo rappresentare le pagine Web come classi Java e utilizzare Page Factory per inizializzare facilmente le variabili WebElement , è ora di vedere come scrivere semplici test Selenium utilizzando il modello Page Object e Page Factory.
Semplice progetto di automazione del test del selenio in Java
Per il nostro tutorial sul modello a oggetti di pagina, automatizziamo la registrazione degli sviluppatori a Toptal. Per fare ciò, dobbiamo automatizzare i seguenti passaggi:
Visita www.toptal.com
Fare clic sul pulsante "Applica come sviluppatore".
Nella pagina del portale prima controlla se è aperta
Fare clic sul pulsante "Unisciti a Toptal".
Compilare il modulo
Invia il modulo cliccando sul pulsante "Unisciti a Toptal".
Impostazione di un progetto
Scarica e installa Java JDK
Scarica e installa InteliJ Idea
Crea un nuovo progetto Maven
Collega "Project SDK" al tuo JDK, ad esempio: su Windows "C:\Programmi\Java\jdkxxx"
Imposta groupId e artefactId:
<groupId>SeleniumTEST</groupId> <artifactId>Test</artifactId>
- Aggiungi le dipendenze Selenium e JUnit Maven nel file POM del tuo progetto
<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>
Sostituisci la versione di Selenium e la versione di JUnit con i numeri di versione più recenti che possono essere trovati cercando JUnit Maven su Google e sul sito di Selenium.
A questo punto, se la compilazione automatica è abilitata, il download delle dipendenze dovrebbe iniziare automaticamente. In caso contrario, attiva Plugin> installa> installa: installa nel pannello Maven Projects sul lato destro del tuo IntelliJ Idea IDE.

Una volta che il progetto è stato avviato, possiamo iniziare a creare il nostro pacchetto di test in "src/test/java". Assegna un nome al pacchetto "com.toptal" e crea altri due pacchetti sotto di esso: "com.toptal.webpages" e "com.toptal.tests".
Manterremo le nostre classi Page Object/Page Factory in "com.toptal.webpages" e le routine di test in "com.toptal.tests".
Ora possiamo iniziare a creare le nostre classi Page Object.
Oggetto Pagina HomePage
Il primo in assoluto che dobbiamo implementare è per la homepage di Toptal (www.toptal.com). Crea una classe in "com.toptal.webpages" e chiamala "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(); } }
Determinazione dei localizzatori di elementi
Sulla homepage di Toptal siamo interessati a un elemento in particolare, ovvero il pulsante "Applica come sviluppatore". Possiamo trovare questo elemento abbinando il testo, che è quello che stiamo facendo sopra. Mentre si modellano le pagine Web come classi Page Object, trovare e identificare gli elementi può spesso diventare un lavoro ingrato. Con gli strumenti di debug di Google Chrome o Firefox, questo può essere reso più semplice. Facendo clic con il pulsante destro del mouse su qualsiasi elemento di una pagina, è possibile attivare l'opzione "Ispeziona elemento" dal menu contestuale per ottenere informazioni dettagliate sull'elemento.
Un modo comune (e il mio preferito) è trovare elementi utilizzando l'estensione FireBug di Firefox, in combinazione con il driver web di Firefox in Selenium. Dopo aver installato e abilitato l'estensione FireBug, puoi fare clic con il tasto destro sulla pagina e selezionare "Ispeziona elemento con FireBug" per aprire FireBug. Dalla scheda HTML di FireBug, puoi copiare XPath, CSS Path, Tag name o "Id" (se disponibile) di qualsiasi elemento della pagina.
Copiando l'XPath dell'elemento nello screenshot sopra, possiamo creare un campo WebElement per esso nel nostro Oggetto Pagina come segue:
@FindBy(xpath = "/html/body/div[1]/div/div/header/div/h1") WebElement heading;
Oppure, per semplificare le cose, possiamo usare qui il nome del tag "h1", purché identifichi in modo univoco l'elemento che ci interessa:
@FindBy(tagName = "h1") WebElement heading;
Oggetto Pagina DeveloperPortalPage
Successivamente, abbiamo bisogno di un Oggetto Pagina che rappresenti la pagina del portale per sviluppatori, che possiamo raggiungere facendo clic sul pulsante "Applica come sviluppatore".
In questa pagina abbiamo due elementi di interesse. Per determinare se la pagina è stata caricata, vogliamo verificare l'esistenza dell'intestazione. E vogliamo anche un campo WebElement per il pulsante "Unisciti a 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(); } }
Oggetto Pagina DeveloperApplyPage
E infine, per il nostro terzo e ultimo oggetto pagina per questo progetto, ne definiamo uno che rappresenti la pagina contenente il modulo di domanda dello sviluppatore. Poiché qui dobbiamo gestire un certo numero di campi modulo, definiamo una variabile WebElement per ogni campo modulo. Troviamo ogni campo in base al loro "id" e definiamo metodi di setter speciali per ogni campo che simulano sequenze di tasti per i campi corrispondenti.
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"); } }
Scrivere un semplice test del selenio
Con le classi Page Object che rappresentano le nostre pagine e le interazioni dell'utente come metodi, ora possiamo scrivere la nostra semplice routine di test come una serie di semplici chiamate e asserzioni di metodi.
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(); } }
Esecuzione del test
A questo punto, la struttura del tuo progetto dovrebbe assomigliare a questa:
Se vuoi eseguire il test, seleziona "ApplyAsDeveloperTest" dall'albero, fai clic destro su di esso e quindi seleziona Esegui "ApplyAsDeveloperTest" .
Una volta eseguito il test, puoi vedere i risultati nell'angolo in basso a sinistra del tuo IDE:
Conclusione
Page Object e Page Factory semplificano la modellazione di pagine Web in Selenium e le testano automaticamente e semplificano notevolmente la vita di sviluppatori e QA. Se eseguite correttamente, queste classi Page Object possono essere riutilizzate nell'intera suite di test e per darti l'opportunità di implementare test Selenium automatizzati per i tuoi progetti sin dall'inizio, senza compromettere lo sviluppo agile. Astraendo le interazioni degli utenti nei modelli a oggetti della tua pagina e mantenendo le tue routine di test leggere e semplici, puoi adattare la tua suite di test alle mutevoli esigenze con il minimo sforzo.
Spero di essere riuscito a mostrarti come scrivere un codice di test bello e pulito che sia facile da mantenere. Concluderò l'articolo con la mia citazione QA preferita:
Pensaci due volte, codifica una volta!