Selenium tapasztalatok Java EE és Primefaces környezetben

Posted by | · · · · | Szoftverfejlesztés | Nincs hozzászólás a(z) Selenium tapasztalatok Java EE és Primefaces környezetben bejegyzéshez

Először a céges önképzés keretein belül találkoztam a Seleniummal. Egy kollégával közösen azt a feladatot kaptuk, hogy ismerjük meg ezt az automatizált tesztelést szolgáló eszközt. Mivel akkoriban épp az egyik nagyobb projektünk tesztelését csináltam, nagyon is kapóra jött ez a feladat. Ki is tűztük elérendő célul, hogy  a napi szintű 30-45 perces manuális folyamattesztet ezentúl a Selenium végezze el helyettem :).

Kicsit a Seleniumról

Azt állítják a készítői az oldalukon, hogy a Selenium automatizálja a böngészőket. Így elvileg bármilyen böngészőből elérhető funkció imitálására képes, azaz kiválóan alkalmas webalkalmazások automatizált tesztelésére is. (De bátran használhatjuk gyakran ismételt manuális folyamatok kiváltására is, ép elménk megőrzésének céljából.)

testing

Ugyan mi a Selenium Webdriver API-t próbáltuk ki, de azért a teljesség kedvéért megjegyezném, hogy létezik egy könnyen használható Firefox addon is. Az adonnal rögzíteni lehet egy weboldalon végzett tevékenységünket, illetve a Selenium által biztosított parancsokkal (Selenese) tovább is bővíthetjük rögzített teszteseteinket. Ezekből a rögzített tesztesetekből pedig egyszerűen exportálhatunk több programozási nyelvbe (pl.: C#, Java, Python, Ruby), amely remek kiindulási pont lehet a tesztek megírásához.

selenium_ide

Page Object Design Pattern

A tesztek felépítésében először is a választott tervezési mintára hívnám fel a figyelmet. A Page Object tervezési minta nagyon elterjedt automatizált tesztelések esetében. Arról szól, hogy az egyes oldalakat külön osztályokként kezeljük, melyeknek a metódusai az oldalon elérhető interakciók. Ennek több haszna is van:

  • Ezáltal jól elkülöníthetőek a tesztelést végző kódok és az oldal specifikus kódok
  • Csökken a duplikációk és hibalehetőségek száma, és fenntarthatóbb a tesztelést végző kód
  • Ha a felületen változik valami, csak a hozzá tartozó Page Object-et kell karbantartani, nem a teszteket egyenként
  • A publikus metódusok az adott oldal által nyújtott szolgáltatásokat reprezentálják
  • Az egyes metódusok a soron következő Page Object-et adják vissza, ezzel is a felhasználó útját modellezve az alkalmazásban. pl: a LoginPage logIn metódusa a MainMenu egy példányát adja vissza:
public MainMenu logIn(String username, String password) {
    usernameInputText.sendKeys(username);
    passwordInputText.sendKeys(password);
    logInButton.click();
    return PageFactory.initElements(driver, MainMenu.class);
}
  • Az ellenőrzéseket a tesztesetekben kell megvalósítani (álalános kivétel ez alól, hogy meggyőződjünk, hogy a WebDriver a megfelelő oldalon van-e éppen)

A fenti példából látható, hogy a WebDriver API biztosít egy factory osztályt a Page Object minta támogatására. Az osztály initElements metódusa példányosítja a paraméterben kapott osztályt, és inicializálja az összes deklarált webelemet, illetve webelemek listáját, a html elem id vagy név attribútuma alapján, de a @FindBy annotáció segítségével egyéb alternatív keresési stratégiát is választhatunk.

Selenium vs. Primefaces

A tesztelendő projektünk a Primefaces (3.0.1) JSF technológiával lett megvalósítva. A Selenium tesztek és a Primefaces-es felületek házasításából jó pár problémás eset született, amelyeket érdemes az utókor számára is feljegyezni.

Hogyan kattintsuk a DataTable megadott sorára?

Szűrhető DataTable esetén szükségünk van arra a szűrő mezőre/mezőkre, mellyel/melyekkel egyértelműen rá tudunk szűrni egy adott sorra. Ezután rákattintunk az egyetlen sorunk bármelyik oszlopára:

nameFilterInputText.sendKeys(name);
WebElement row = driver.findElement(By.cssSelector(".ui-datatable-data tr td.name"));
row.click();

Miért nem választja ki a SelectOneMenu a kiválasztott opciót?

SelectOneMenu egy opciójának kiválasztására próbáltam rábírni a Seleniumot, és elkövettem azt a hibát, hogy hittem a szememnek. A teszt futtatásakor meglepődve tapasztaltam, hogy az említett kiválasztáskor nem történik semmi a böngészőmben. Egy kis hibakereséssel eltöltött idő után jöttem rá, hogy a Primefaces jótékonyan elfedi előlünk a kiválasztás folyamatát, de ettől nem kell megijedni, a háttérben azért megtörténik. Legegyszerűbb felhasználási módja:

@FindBy(id = ”selectOne”)
private WebElement selectOne;

public void select(String option){
    Select select = new Select(selectOne);
    select.selectByValue(option);
}

Miért tűnik el az AutoComplete-ba írt érték focus vesztés után?

AutoComplete komponens kitöltésekor ért az a meglepetés, hogy ha “csak úgy” beírjuk a mezőbe a szöveget, akkor az eltűnik focus vesztés után. Ezért a kitöltés metódusát felruháztuk felhasználói viselkedés móddal 🙂

public void setCustomer(String name) {
    customerNameInput.sendKeys(name);
    actions.waitUntilElementExists(By.className("ui-autocomplete-item"));
    customerNameInput.sendKeys(Keys.TAB);
}

Hogyan kattintsunk Menubar és MenuButton nem első szintű elemére?

Több szintű Menubar és MenuButton elemeknél jött elő, hogy nem is olyan egyszerű rákattintani egy-egy alsóbb szintű elemre. Ezt a problémát az első szintű elem „vonszolásával” sikerült kiküszöbölni :

public void clickMenuItem(WebElement element) {
    Actions builder = new Actions(driver);
    builder.clickAndHold(element).moveByOffset(5, 5).build().perform();
    subElement.click();
}

Ez után már rá lehet kattintani  vonszolt elem alá tartozó elemekre is.

FileUpload elhelyezkedési problémája:

Egy fájl feltöltéséhez a FileUpload inputjára kell elküldeni a feltölteni kívánt fájl abszolút elérési útvonalát. A problémát a Primefaces-es FileUpload inputjának elhelyezkedése okozta, ugyanis az balra kilógott a komponens alól, így a Selenium nem tudott „belekattintani”.
fileuploadbug
Ennek kiküszöbölésére az alábbi trükköt alkalmaztuk:

public void primefacesFileUpload(WebElement webElement, String path) {
    String script = "$('.fileupload-buttonbar .ui-button input')
                                    .css('position','absolute')
                                    .css('top','0px').css('left','0px')";

    JavascriptExecutor jsExec = (JavascriptExecutor) driver;
    jsExec.executeScript(script);
    webElement.findElement(By.cssSelector("input")).sendKeys(path);
    waitUntilAjaxRequestCompletes();
}

Azaz a rejtett InputText-et a FileUpload alá “toljuk”, hogy a kattintás sikerülhessen.

Pár hasznos várakozó metódus, amit menet közben alkalmaztunk:

/**
* Vár az aktív ajax jQuery kérések befejezéséig
*/

public void waitUntilAjaxRequestCompletes() {

    // várunk egy másodpercet
    firstPollingDelay();

    new FluentWait(driver).withTimeout(30, TimeUnit.SECONDS)
                                     .pollingEvery(3, TimeUnit.SECONDS)
                                     .until(new ExpectedCondition() {

        public Boolean apply(WebDriver d) {
            JavascriptExecutor jsExec = (JavascriptExecutor) d;
            Boolean result = (Boolean) jsExec.executeScript("return $.active == 0;");
            return result;
        }
    });
}
/**
* Vár az elem megjelenéséig
*
* @param a megjelenítésre váró elem
*/

public void waitUntilElementExists(final By by) {

    firstPollingDelay();

    new FluentWait(driver).withTimeout(30, TimeUnit.SECONDS)
                                     .pollingEvery(2, TimeUnit.SECONDS)
                                     .until(new ExpectedCondition() {

        public Boolean apply(WebDriver wd) {
            return !wd.findElements(by).isEmpty();
        }
    });
}

Utolsó szó jogán

Az önképzésünk sikeresen lezárult, elkészültek a futtatható tesztek. A korábbi 30-45 perces manuális kattintgatás helyett ~10 perc alatt fut le automatikusan a 11 db teszt eset. Ebben a betanuló projektben ugyan benne van kb. egy hónap munka. A megszerzett tudást egy másik projektben kamatoztatva már kb. 1,5 hét alatt sikerült megírni a teszteket, és ez az idő múlásával egyre jobban megtérül.
A tesztjeinket Maven build eszköz segítségével Embedded Glassfish-re indított alkalmazáson, Embedded Derby által kiszolgált ismert adatbázis tartalommal használjuk. Hudson continuous integration rendszeren ütemezett futtatással egy gyors visszacsatolást kapunk, ha a tesztesetekkel lefedett alapfunkcionalitásban bármi elromlana. Ezzel pedig sok kellemetlenségtől kímélhetjük meg magunkat.


No Comments

Leave a comment