2011. december 18., vasárnap

Karácsonyi kiadás: JBoss AS vs házi macska

Karácsony alkalmából most egy rendhagyó bejegyzéssel jelentkezem, ahol is annak próbálok utánajárni, hogy milyen hasonlóságok és eltérések lehetnek egy JBoss alkalmazás szerver és egy kövér házi macska között. Lássuk...


Java alapú alkalmazás szerver Javarészt alszik és fetreng
Néha bugos Néha bolhás
Többféle tanúsítvánnyal is rendelkezik Egy oltási könyvvel rendelkezik
Gyorsan indul Lassan indul
Stabilan fut Stabilan fut
Memória fogyasztása meglepően alacsony Étel fogyasztása meglepően magas
További JBoss szerverekkel is képes együttműködni Más macskákkal nem képes együttműködni
Vasat tolok alá Párnát tolok alá
Kiszolgálja a kéréseket Kiszolgálom a kéréseit

Boldog Karácsonyt és Kellemes Ünnepeket Kívánok a blogom olvasóinak!



2011. december 1., csütörtök

Becsüld meg a szoftverben maradt hibák számát!

A szoftverekben mindig is lesz hiba amíg azokat emberek írják, ezért a szoftver készítése során - nem csak az átadás előtt - nagy figyelmet kell fordítani a folyamatos tesztelésre. Függetlenül attól, hogy a tesztelést maguk a fejlesztők vagy egy tesztelésre dedikált csapat végzi, nem garantálható hogy az összes hiba felderítésre is kerül. Sajnos a gyakorlatban nem lehet mindent 100%-osan letesztelni leginkább idő és pénzhiány miatt, így fel kell arra készülnünk, hogy az ügyfélnek átadott szoftverben maradnak majd hibák. Ezek a hibák lehetnek alacsony súlyú szöveges elírások vagy akár kritikus szintű funkcionális hibák.


Hasznos lenne, hogyha valahogyan következtetni tudnánk a szoftverben maradt hibák számára és ezek súlyosságára. Fontos kérdés lehet az is, hogy meddig folytassuk az alkalmazás tesztelését, azaz mikor jelenthetjük ki nyugodt szívvel, hogy a szoftver nagy valószínűséggel nem tartalmaz már kritikus hibákat.

Az előbbi kérdésekre a válaszom, hogy tegyünk szándékosan a szoftverbe különböző súlyosságú, könnyen és nehezen előidézhető bugokat, majd nézzük meg hogy a tesztelőink ebből hányat találnak meg. Elsőre lehet kicsit furcsának tűnik ez a megközelítés, azonban a szándékosan rögzített felderítetlen hibák száma arányos lesz a rendszerben maradt valódi hibák számával.

2011. november 19., szombat

Lejárt JRebel licence ingyenes megújítása

Nemrég írtam egy ismertetőt a JRebel-ről, de akkor nem említettem hogy sajnos csak 30 napig használható ingyenesen. Ha ezután is használni szeretnénk, frissíteni kellene a licence-t, ami bizony komoly költséget is jelenthet. Szerencsére erre is van megoldás - méghozzá kettő is -, így most megosztom ezeket a blogom olvasóival!

Az első lehetőség, hogy kérünk a JRebel-től egy új licence-t. Persze nem a meglévő meghosszabbítását, hanem egy újat! :) A JRebel legelső telepítését normál módon végezzük el a manual alapján, amihez használhatjuk a jar installert vagy az Eclipse plugin-on keresztüli telepítést is. Miután lejárt a licenszünk, látogassunk el a http://www.zeroturnaround.com/jrebel/eclipse-eval/ oldalra, ahonnan igényelhetünk egy újat. Itt csupán annyi a megkötés, hogy egy e-mail címet csak egyszer regisztrálhatunk be az új license kéréséhez. Használjuk a YOPMail-t. Ezután email-ben csatolva megkapjuk a jrebel.licence fájlt amit másoljuk be a JRebel telepítési könyvtár alá, ami nálam pl. a /usr/local/ZeroTurnaround/JRebel könyvtár. Ezzel a kis trükkel újabb 30 napig használhatjuk a JRebel-t! Szuper! :)

A második lehetőség pedig a JRebel Social használata, amit bárki ingyenesen alkalmazhat nem kereskedelmi jellegű alkalmazások fejlesztéséhez. Örülök, hogy a JRebel Team meglépte ezt a lépést a Java közösség érdekében!


2011. november 1., kedd

Adatforrás kialakítása Selenium tesztekhez

A mostani cikkemben a FitNesse és a Selenium keretrendszerek egy olyan előnyös ötvözéséről fogok blogolni, amit jómagam találtam ki a Selenium-os tesztelés megkönnyítésére!

Gyakori igény, hogy szükségünk lenne a Selenium tesztjeink eltérő adatokkal való futtatására. Az adatok tárolására elsőre jó megoldásnak tűnhet egy egyszerű csv, properties vagy excel táblázat használata, azonban több száz teszt esetén ezek nem lesznek karbantarthatóak (gondoljunk csak bele, hogy milyen nagy állományok jönnének létre), főleg amikor eltérő környezetekhez eltérő adatokat kellene használnunk. Egy javítási mód lenne ha nem egy nagy excel táblázatot kezelünk, hanem környezetenként (esetleg tesztenként) létrehoznánk egyet-egyet, azonban egy idő után ezzel is karbantartási problémák merülnének fel, mivel a sok állomány manuális kezelése elég nehézkes!

A felvázolt problémára szerintem egy webes oldalstruktúrát használó, HTML táblázat alapú adatforrás jelenthetné a megoldást. Mivel kifejezetten erre a célra nem készült még keretrendszer, úgy gondoltam, - az eredeti felhasználási területétől egy kicsit elrugaszkodva - hogy bevetem a FitNesse-t!

Excel alapú Selenium adatforrás

Bár nagyszámú teszteset esetén az excel alapú adatforrás véleményem szerint nem a legmegfelelőbb választás, bizonyos esetekben megfelelő alternatíva lehet az egyszerű és gyors kezelhetősége miatt. Az excel alapú adatforrás megvalósításának részleteiről itt olvashatsz bővebben.

FitNesse alapú Selenium adatforrás

A Selenium és a FitNesse integrációjával nemcsak az adatforrás problémakörre kapunk megoldást, hanem egy-két nagyon hasznos feature-re is szert teszünk:

  • Tesztjeinket a FitNesse webes felületéről is elindíthatjuk és megtekinthetjük lefutási eredményeket.
  • A FitNesse wiki oldalak verziózásából adódóan, visszaállhatunk egy korábbi adatforrás verzióra.
  • A tesztek definiálása és az adatok megadása mellett, akár szöveges megjegyzéseket is hozzáadhatunk az adatforrás oldalaihoz.
  • Visszakövethetők azok a felhasználók, akik az adatforrás oldalain módosításokat végeztek.
  • Az oldalak szerep alapú korlátozásának lehetősége az olvasás, írás és teszt futtatás tekintetében.

A FitNesse alapú adatforrás kialakításának további előnye, hogy az eltérő környezetekben futó tesztek (pl.: Teszt, Fejlesztői, Integrációs) nemcsak eltérő adatforrásokat, hanem egyedi paramétereket is használhatnak melyeket a hierarchia tetején is definiálhatunk az öröklődésük miatt.


A fenti ábra a FitNesse segítségével definiált, Selenium tesztekhez kapcsolódó adatforrások elhelyezkedését mutatja. A RootSuite tartalmazza a környezetek szerinti suite-okat (TestSuite, DevSuite, IntSuite), melyek a tesztesetek adatforrásait tartalmazzák. A kialakított adatforrás felépítés következtében, a tesztek a gyökér suite-nál egyszerre, vagy környezetenként külön-külön is indíthatók. Érdemes a RootSuite-nál meghatározni a Fixture classpath-t és a használni kívánt böngésző típust (driver), a környezeteknek megfelelő suite-oknál pedig a baseUrl-t, mivel a szülő csomópontoknál megadott paramétereket tovább öröklődnek a gyerekek felé.

Az adatforrások létrehozása megegyezik a FitNesse teszt táblázatok felvételével, a különbség annyi hogy a tesztmetódusok visszatérési értékét nem ellenőrizzük, így azok egységesen true-val térnek vissza. Természetesen a sikertelenül lefuttatott tesztesetek ilyenkor is kijelzésre kerülnek. 


A környezetenként eltérő ${baseUrl} és a böngésző meghajtóhoz tartozó ${driver} változóként kerül definiálásra, mivel az értékek a hierarchiában feljebb lévő suite-nál kerültek definiálásra.


Az elkészített wiki oldalhoz tartozó adatokat a LoginTest, testLoginFunction metódusa használja fel. A tesztosztály a TestBase ősosztályból származik, ahol inicializálásra kerül a baseUrl és a böngésző típus. A TestBase egyben egy ColumnFixture is, amit a RootSuite-nál definiált classpath bejegyzés köt össze a wiki oldallal.
public class TestBase extends ColumnFixture {

 protected Selenium selenium;
 protected WebDriver driver;

 protected void beforeTestCase(String[] fixtureArgs) {
        String baseUrl=fixtureArgs[0];
        String driverName=fixtureArgs[1];
        
        if(driverName.toLowerCase().equals("firefox")){
            driver = new FirefoxDriver();
        }
        else if(driverName.toLowerCase().equals("chrome")){
            driver = new ChromeDriver();
        } 
        //...

        selenium = new WebDriverBackedSelenium(driver, baseUrl);
    }

 protected void afterTestCase() {
  driver.close();
 }
}
Az így kialakított felépítés nem egy hagyományos JUnit teszteset, így a beforeTestCase() ill. afterTestCase() metódusokat a tesztmetódusban kell meghívni. Az afterTestCase() metódus a finally blokkban kapott helyet, így annak lefutása mindig garantált, azaz a Selenium által megnyitott böngésző egy esetleges kivétel esetén is bezárásra kerül. A teszt elindításával a LoginTest publikus mezői megkapják - a táblázat minden sorára lefutva - a wiki táblázat megfelelő mezőinek értékét, megoldva a külső adatforrás értékeinek a felhasználását.
public class LoginTest extends TestBase {

 public String userName;
 public String password;

 public boolean testLoginFunction() throws Exception {
  beforeTestCase(getArgs());
  try {
   selenium.open("/portal/ep/home.do");
   //További selenium parancsok vagy PageObject-ek használata
  } finally {
   afterTestCase();
  }
  return true;
 }
} 
Ezzel egyelőre véget is ért a Selenium-os cikksorozatom. Ha hasznos infókat tartalmazott a számodra, esetleg további kérdéseid lennének a Seleniummal kapcsolatban, írj egy kommentet!

2011. október 17., hétfő

Page Object minta alkalmazása Seleniummal

A múltkori elméleti bevezető után jöjjön egy kis gyakorlat a Page Object tervezési minta Seleniummal való használatához.

Nézzünk is meg egyből egy rövid kódrészletet, amit mindenféle módosítás nélkül a Selenium IDE-ből exportáltam ki! Látható hogy a Selenium parancsok és az ellenőrzési feltételek nem válnak el egymástól, továbbá a várakozási feltétel implementációja sem valami jól olvasható, ami pedig kifejezetten zavaró ha a teszteseteink ezzel vannak tele.
selenium.open("/portal/ep/home.do");
selenium.type("password", password);
selenium.type("username", userName);
selenium.click("css=input[type=image]");
for (int second = 0;; second++) {
 if (second >= 60)
  fail("timeout");
 try {
  if ("szöveg1"
    .equals(selenium.getText("css=#sfeBranchDipatch > div")))
   break;
 } catch (Exception e) {
 }
 Thread.sleep(1000);
}
assertEquals(selenium.getText("css=strong"), "Szia " + userName);

Miután a fenti kódot átírtam a Page Object minta alkalmazásával, egy jól olvasható tesztesetet kaptam. Azt hiszem a két kódrészletet összehasonlítva az eredmény magáért beszél. A továbbiakban lássuk mi is bújik meg a Seleniumos Page Objectek mögött!
Home home = new Home(selenium);
InfoPortal infoPortal = home.loginUser(userName, password);
assertEquals(infoPortal.getHeaderMsg(), "Szia " + userName);
AppPage appPage = infoPortal.getMenu().goToAppPage(selenium);
Először is érdemes minden Page Object-et egy Page osztályból származtatni, ahova az oldalakkal kapcsolatos közös részeket emeljük ki. A valódi webes oldalakhoz hasonlóan ahol is minden oldalon megjelenik egy menüsor a Page osztályból is elérhető egy Menu objektum, amely a navigációhoz szükséges menüelemeket tartalmazza. Ezenkívül itt kap helyet egy Selenium objektum is, melyet a Page Object-ek konstruktoraiban adunk majd mindig át.
public class Page {
 protected Selenium selenium;
 private Menu menu;

 public Page(Selenium selenium) {
  this.selenium = selenium;
  menu = Menu.getInstance();
 }

 public Menu getMenu() {
  return menu;
 }

 public void setMenu(Menu menu) {
  this.menu = menu;
 }
}
A Menu osztály a Singleton tervezési mintára épül, mivel a tesztesetek egészére nézve globálisan csak egy példányra lesz belőle szükségünk! 
public class Menu {
 private static Menu menu = null;

 private Menu() {
 }

 public static Menu getInstance() {
  if (menu == null) {
   menu = new Menu();
  }
  return menu;
 }

 public AppPage goToAppPage(Selenium selenium) {
  selenium.click("css=#mainTab3 > span");
  return new AppPage(selenium);
 }
}
Az adatforrásból kinyert mezők paraméterátadás útján jutnak el a Page Object-ekben található akció metódusokhoz, mint amilyen az Home loginUser() metódusa. Fontos megkötés, hogy az akciók visszatérési értéke mindig a következő oldalt jelentő page osztály legyen, ami jelen esetben az InfoPortal.
public class Home extends Page {
 public Home(Selenium selenium) {
  super(selenium);
 }

 public InfoPortal loginUser(String userName, String password)
   throws Exception {
  selenium.open("/portal/ep/home.do");
  selenium.type("password", password);
  selenium.type("username", userName);
  selenium.click("css=input[type=image]");
  waitForElementPresent("css=#sfeBranchDipatch > div",
    "Az kívánt oldal ….");
  return new InfoPortal(selenium);
 }
}
A Page Object minta egyik alapvető koncepciója, hogy a Page osztályok ne tartalmazzanak ellenőrzési feltételeket, azaz asserteket. Ez alól kivételt jelentenek az olyan jellegű megkötések, melyeknek az oldalra lépéskor mindig teljesülnie kell. Ilyen ellenőrzési feltétel található az InfoPortal konstruktorában, amely feltételezi hogy a kijelentkezés link mindig elérhető az adott oldalon.
public class InfoPortal extends Page {
 public InfoPortal(Selenium selenium) {
  super(selenium);
  assertEquals(
  selenium.getText("//table[@id='countDownTable']/tbody/tr/td[2]/a"),
  "Kijelentkezés");
 }

 public String getHeaderMsg() {
  return selenium.getText("css=strong");
 }
}
Ahogy korábban említettem, a waitFor kezdetű parancsokhoz a Selenium IDE nem generál valami szép kódot, így a kód olvashatósága miatt és a duplikációk megszüntetése érdekében ezeket érdemes kiemelni egy külön segéd osztályba vagy akár a közös Page osztályba.

Röviden ennyi a Page Object minta implementálásáról, a következő bejegyzésem pedig már a zárócikke lesz a Selenium-os sorozatomnak. Elöljáróban annyit, hogy a Selenium-os adatforrás kialakításról lesz szó, azonban egy olyan egyedi megoldást fogok bemutatni, amivel máshol még biztos nem találkoztál!

2011. október 2., vasárnap

Karbantartható Selenium tesztek készítése

A Selenium IDE segítségével kiexportálható teszteset tipikusan struktúra nélküli "spagetti kód", melyet nehéz olvasni és bővíteni, továbbá az oldalon végrehajtott módosítások is könnyen elronthatják az összes eddigi tesztjeinket a megváltozott lokátorok miatt.

Tegyük fel, hogy készítettünk 100 Selenium tesztesetet melyek mindegyike egy bejelentkező oldalról indul ki. Ha a bejelentkező oldalon, a felhasználónév megadására szolgáló input mező fölé bekerülne egy új input mező és az XPath position alapú lokátor stratégiát használtuk, akkor a //div/input[0] helyett //div/input[1] –re változik a felhasználó név mezőhöz tartozó relatív XPath kifejezés. Mivel mind a 100 teszteset erről az oldalról indul ki, ezért az összes tesztünket módosítani kellene, ami elég időigényes feladat.

Az előbb vázolt problémából kiindulva, az első gondolat az Extract Method minta alkalmazása lenne, miszerint a tesztosztályon belüli ismétlődő kódrészeket kiszervezhetnénk külön private metódusokba. Ezzel viszont az lenne a gond, hogy a kód duplikációt csak az osztályon belül kezelnénk le, azaz a tesztosztályok közötti kódismétlődés ettől még nem szűnne meg. A megoldás a Page Object minta alkalmazása, amellyel az oldalakhoz tartozó kódrészek, a nekik megfelelő un. page osztályokba kerülnek kiszervezésre.

Ha megvizsgálunk néhány Selenium IDE által rögzített tesztesetet, megállapítható hogy akciók hatására oldalakra navigálunk és az oldalakon ellenőrző feltételeket értékelünk ki. Az alábbi ábrán az akciókat a piros dobozok, míg az oldalakat a kék karikák szemléltetik.


A Page Object minta szerint, az egyes oldalakhoz tartozó műveleteket külön Page osztályokba kell kiszervezni (pl.: bejelentkeztetés -> LoginPage), az ellenőrző feltételek és az oldalak közti navigációs kódok pedig a tesztmetódusokban kapnak helyet, mivel így biztosítható a Page Objectek későbbi újrahasználhatósága. Az alábbi ábrán látható szekvencia diagram jól szemlélteti a Page Object minta Selenium környezetben való használatát.


A fenti ábra alapján, a tesztmetódus meghívja a LoginPage "Page Object" bejelentkeztető metódusát átadva a felhasználó nevet és a jelszót, majd a beléptetés után a következő oldalt jelentő HomePage "Page Object" példánnyal tér vissza, melyen a tesztmetódus meghívja annak openTab() metódusát. Bár az ábrán nincs jelölve, a bejelentkezés után visszakapott HomePage objektumon a tesztmetódus ellenőrzési feltételeket végezhet a kezdőlapon megjelenő felhasználó név helyességére vagy a felhasználó által látható menüelemekre vonatkozólag.

Visszatérve a fejezet elején említett problémára, ha a Page Object tervezési mintát alkalmazzuk, akkor a //div/input[0]-ről, //div/input[1]-re történő megváltozás esetén, nem kell mind a 100 tesztesetünket módosítani, csupán a LoginPage "Page Object" bejelentkeztető metódusánál kell átírni a felhasználónév input mező kikeresésére szolgáló lokátort.

Összefoglalás a Page Object minta használatáról
  • Oldalanként vagy oldalon belüli komponensekre tovább bontva hozzunk létre a Page Object-eket, melyek az akciókat tartalmazzák.
  • A Page Object akció metódusainak visszatérési értéke mindig ahhoz az oldalhoz tartozó Page Object legyen, ahová a navigáció irányítja majd a felhasználót.
  • Mindig a tesztesetek tartalmazzák a visszakapott Page Objectre – vagyis az oldalra - vonatkozó ellenőrzési feltételeket.
  • A Page Object-ek ne tartalmazzanak ellenőrzési feltételeket, kivéve olyan jellegű assertek-et, melyek az egész oldalra vonatkoznak, és mindig teljesülniük kell. (pl.: az adott oldalon megjelenő böngésző címe, aktív menü ellenőrzése az oldalra lépésekor).
  • A tesztesetek ne tartalmazzanak Selenium specifikus kódot, az assertek végrehajtásához szükséges lokátor kódot illesszük be a megfelelő Page Object-be, getter metódusként.
  • Kezeljük kiemelt fontossággal a Page Object-ek névadását, hogy megkönnyítsük azok beazonosítását!

A Page Object minta előnyei és hátrányai
  • Duplikációk kiküszöbölése azáltal, hogy a módosítások esetén csak a Page Objectekhez kell hozzányúlni a teszt metódusokhoz nem.
  • A tesztesetekben nem jelennek meg Selenium specifikus kódrészletek, így a mögöttes Selenium API bármikor lecserélhető. Ez a későbbiekben megkönnyíti majd a Selenium 2 API használatára történő átállást.
  • Újrafelhasználhatóság biztosítása azáltal, hogy a tesztkód és az oldalhoz tartozó kód elválik.
  • Kellő számú Page Object létrehozása után, automatikusan kialakul egy saját API, amellyel azonnal létrehozhatunk a felületi teszteket a Selenium IDE használata nélkül is!
  • A minta hátránya, hogy a fejlesztőtől többletmunkát igényel a Page Object struktúra kialakítása, ami persze a későbbiekben bőven megtérül.

A következő bejegyzésemben a Page Object minta Selenium környezetben való alkalmazását fogom bemutatni egy konkrét példán keresztül.

2011. szeptember 15., csütörtök

Selenium - Lokátor stratégia kiválasztása

Az előző bejegyzésemben a Selenium IDE parancsairól írtam, azonban arról még nem esett szó hogy a felvett parancsok hogyan is mutatnak rá a html elemekre. A mostani bejegyzésem tehát a Selenium lokátorokról fog szólni, kiemelve a JSF-es alkalmazások lokátor stratégia választását.


A Selenium lokátorok lehetővé teszik, hogy kiválasszuk azt a html elemet amelyre a Selenium parancsot végre akarjuk hajtani. A parancshoz tartozó lokátor a Selenium IDE target mezőjében jelenik meg. A html elemek kijelölésére többféle lokátor stratégia közül is választhatunk, melyeket lokátorTípus=lokátor formában tudunk megadni, bár a lokátor típus megadása nem minden esetben kötelező. Az alábbi táblázat a fontosabb lokátor stratégiákat szemlélteti egy-egy példán keresztül:

Lokátor stratégia Példa
Identifier basedloginForm:userNameId
Name AttributeloginForm:userName
CSS basedcss=#usernameInputStyle
DOM Index document.forms[0].elements[1]
XPath Attribute//input[@id='loginForm:userNameId']
XPath Id Relative//div[@id='userNameId_inputTextOuterDiv']/input
XPath Position//div/input

A Selenium IDE Locator Assistance funkciója lehetőséget ad arra, hogy a rögzített parancsokhoz tartozó alapértelmezésként felvett lokátort egy másikra módosítsuk. Ez a feature pedig pont jól fog jönni a JSF alapú alkalmazások Selenium teszteléséhez. A Locator Assistance funkciót a Selenium IDE target combobox lenyitásával érhetjük el. A target mezőnél akár saját kifejezést is megadhatunk, de arra figyeljünk, hogyha a lokátor több html elemre is illeszkedne az oldalon, akkor a legelsőnek megtalált elem kerül kiválasztásra.



Lokátor stratégia kiválasztása JSF alapú alkalmazásokhoz

Általános esetben az azonosító alapú lokátor stratégia használata javasolt, mivel a html elem azonosítója mindig egyértelműen beazonosítja az elemet, így az oldal későbbi átstrukturálása esetén is ugyanarra az elemre fog mutatni, továbbá a html elem megtalálása szempontjából is az azonosító alapú stratégia tekinthető a leggyorsabbnak hasonlóan a name attribútum alapú lokátor stratégiához.

JSF-es alkalmazások esetén sajnos ez nem teljesen igaz, mivel a gyerek elemek azonosító képzésénél a szülő elemek azonosítói is szerepet játszanak. Nézzük az alábbi példát, ahol is egy JSF/RichFaces-es kódrészlet és az ebből kirenderelt html kód látható:

  
  

A html kimenet input azonosítóinak és name attribútumainak generálásában a szülő form azonosítója is részt vesz, így ha nem adunk meg azonosítót, az id a html kimenetnél nem fog szerepelni, valamint a name attribútum esetében kiegészül egy generált j_id32 értékkel.

A következő JSF/RichFaces ill. generált html kódrészlet egy olyan átstrukturálást szemléltet ahol is a form és az input elemek közé bekerült egy a4j:repeater elem. Ebben az esetben a generált html input azonosítói is megváltoznak, mivel az azonosítókhoz hozzáadódik egy ar:sorszám rész is az a4j:repeater azonosítójától. 

      
    
    
  

A felvázolt problémakör után lássuk, hogy milyen lokátor stratégiák közül érdemes választani a JSF-es alkalmazások Selenium-os teszteléséhez. A választás függ a környezettől, így a végső döntéskor az alábbi szempontok szerint mérlegeljünk.

1. Azonosító vagy name attribútum alapú lokátor stratégia
  • A leggyorsabb lokátor stratégia.
  • Használatához az azonosítókat mindig meg kell adni a JSF kódban, különben az id attribútum nem generálódik ki, a name attribútumnál pedig j_id123 formátumú azonosítók generálódnak melyekre a tesztek készítése során nem hagyatkozhatunk a változékonyságuk miatt.
  • Az oldal módosítására és átstrukúrálására nem annyira érzékeny. Új elem felvétele esetén (pl.: új input felvétele a form alá) az azonosítók nem módosulnak (kivéve datatable, repeater beszúrásakor). A form-on kívül definiált elemek legtöbbször nincsenek hatással a form-on belüli azonosítókra.

2. XPath Position alapú lokátor stratégia
  • Internet Explorer alatt akár jelentős lassulás is tapasztalható a tesztek futtatása során, az elemek kikeresésekor.
  • Használatához az azonosítókat nem kötelező megadni, mivel ez a stratégia a html elemek sorrendiségére és elhelyezkedésére épít.
  • Az oldal módosításra teljes mértékben érzékeny. Változtatás esetén szinte mindig megváltozik a html elemek sorrendisége vagy elhelyezkedése, így az elemekhez tartozó XPath kifejezések is. Ez azt jelenti, hogy a JSF kód átstruktúrálása esetén a parancsokhoz tartozó XPath lokátorokat újra fel kell venni.
  • Abszolút XPath kifejezések helyett érdemes relatív XPath kifejezéseket használni.

3. XPath ID Relative lokátor stratégia
  • Azonosító és XPath position alapú lokátor stratégia ötvözése.
  • Használjunk relatív XPath kifejezéseket, melyeket egy fix azonosítótól kezdve adjunk meg.
  • Válasszunk az elemhez képest minél közelebbi fix azonosítót és minimalizáljuk az XPath kifejezés hosszát, így a módosításokra sem lesz annyira érzékeny.

A JSF-es alkalmazások Seleniummal való tesztelésénél - beleértve a RichFaces, IceFaces, PrimeFaces-es webes keretrendszereket - tehát annyi dolgunk van, hogy az említett három stratégia közül kiválasztjuk az igényeinknek legmegfelelőbbet, majd a Selenium Locator Assistance funkciójával átállítjuk a Selenium parancsokhoz tartozó lokátor stratégiát. Mivel a Selenium IDE-ben alapértelmezett lokátor stratégia választásra nincs lehetőség, ezért egyelőre ezt manuálisan kell elvégezni esetleg módosíthatjuk a Selenium IDE kódjában a default beállítást!


A Selenium-os cikksorozatom következő részében megmutatom, hogy hogyan készíthetünk újrafelhasználható teszteket a Selenium IDE-ből kiexportálható spagetti kód helyett!

2011. augusztus 26., péntek

Selenium IDE parancsai

A bevezető után ahogy ígértem, a Selenium IDE parancsok áttekintése következik a referencia alapján.

A Selenium IDE által rögzített teszteset Selenium parancsok sorozatából áll, melyek lehetnek akciók, állapot tárolók vagy ellenőrzési feltételek. Amíg az akciók a Selenium IDE által automatikusan rögzítésre kerülnek, addig az állapot tárolókra és az ellenőrzési feltételekre vonatkozó parancsokat a böngészőben egy kívánt elemen, a jobb egérgombos kattintással megjelenő gyorsmenüben van lehetőségünk hozzáadni.

1. Akciók

Az akciók olyan parancsok, melyek az oldal állapotát valamilyen módon megváltoztatják. Minden akcióknak van egy ”AndWait” végződésű változata is melyeket akkor kell használni, amikor az akció végrehajtása egy új oldal betöltését vagy az aktuális frissítését fogja eredményezni.

Művelet Akció Akció, majd várakozás az oldal betöltésére
Kattintás click clickAndWait
Szöveg begépelése type typeAndWait
Elem kiválasztása (pl.:checkbox) select selectAndWait

2. Állapot tárolók

Állapot tárolók használatával lehetőségünk van az oldalon lévő információkat (pl.: szöveg) elmenteni, melyekre később hivatkozhatunk ugyanezen tesztesetnél. Állapot tárolókhoz a store kezdetű parancsokat (pl.: storeText, storeTitle) használhatjuk.

3. Ellenőrzési feltételek

Az ellenőrzési feltételek feladata hogy ellenőrizzék az oldal állapotát, anélkül hogy azt módosítanák. Az ellenőrzési parancsok három csoportba sorolhatók: assert, verify és waitFor kezdetűek.

Assert és a verify használata

Az assert és verify kezdetű parancsok közt az a különbség, hogy hiba esetén az assert használatakor a teljes teszteset futása leáll, míg a verify kezdetű parancsoknál a teszt futása tovább folytatódik, a hiba pedig naplózásra kerül.
Az ellenőrzési parancsokat érdemes logikailag csoportokba szervezni, ahol is először egy assert kezdetű paranccsal ellenőrizzük, hogy a megfelelő oldalon vagyunk-e (pl.:assertTitle) a többi ellenőrzési feltétel pedig verify kezdetű parancs, melyek az oldalon megjelenő mezők helyességét vizsgálják. Tehát ha rossz oldalon vagyunk, akkor felesleges a többi mezőt is ellenőrizni, ha pedig az oldal jó, akkor egy hibás mező esetén a teszt még tovább folytatódhat a hiba kijelzésével.

Ellenőrzési módszerek

Ellenőrizendő feltétel Minta parancsok
Adott szöveg jelen van valahol az oldalon?verifyTextPresent, assertTextPresent
Adott html elem jelen van valahol az oldalon?verifyElementPresent
Adott szöveg, meghatározott helyen jelen van az oldalon?verifyText, verifyNotText, assertText

A pontosabb ellenőrzés érdekében a verifyText parancsot részesítsük előnyben.

Ajaxos oldalak ellenőrzése

Ajaxos alkalmazások tesztelésénél figyelni kell arra, hogy egy ajaxosan előtűnő elem ellenőrzését megelőzően felvegyünk egy olyan parancsot is, mellyel megvárjuk annak megjelenését. A frissülő elemek a teljes oldal újratöltése nélkül kerülnek megjelenítésre, így az akcióknál ismertetett andWait végződésű parancsok nem használhatók. A waitFor kezdetű parancsokat kell használni melyeket az alábbi táblázat foglalja össze.

Működés Minta parancs
Várakozás egy adott szöveg megjelenésére az oldalonwaitForTextPresent
Várakozás egy adott html elem megjelenésére az oldalonwaitForElementPresent
Várakozás egy adott szöveg, adott helyen történő megjelenésérewaitForText

Tehát egy ajaxosan megjelenő elem ellenőrzéséhez először mindig vegyünk fel egy waitFor kezdetű parancsot, ami után felvehetünk további verify kezdetű parancsokat.

A következő bejegyzésemben a Selenium lokátor stratégiáiról fogok blogolni és segítek eldönteni, hogy a JSF alapú alkalmazásokhoz melyiket is válasszuk!

2011. augusztus 16., kedd

Selenium - Bevezetés a webes felületi teszteléshez

Frissítve: 2011.11.04.  

Gyakori, hogy az elkészült webes felületeket az erre szakosodott tesztelő csapat egy előre meghatározott tesztforgatókönyv alapján manuálisan klikkelgeti végig, ahelyett hogy valamilyen automatizmust használnának!

A projekt kezdeti fázisában - ahol még a felület gyakran változik - a manuális felületi tesztelés lehet a célravezetőbb, a későbbiekben azonban mindenféleképpen érdemes elgondolkozni valamilyen automatizált megoldás bevezetésén. Véleményem szerint a Seleniumos tesztek készítése akkor éri meg a leginkább, amikor a felület már nem nagyon módosul, de a mögöttes logika, szolgáltatások vagy a háttér rendszerek fejlesztése még folyamatban van. Seleniumos felületi tesztek készítésével a tesztek bármikor visszajátszhatók akár Hudsonnal integrálva is, így szükség szerint folyamatosan értesülhetünk a tesztjeink lefutási eredményeiről!

A webes felületek automatizált tesztelése azonban nem tekinthető triviálisnak, gondoljunk csak a böngészőkből adódó eltérésekre, vagy az ajaxos alkalmazások újszerű kihívásaira. A következő néhány bejegyzésemben ezt a témakört fogom a Selenium keretrendszer bemutatásával körbejárni és megmutatom, hogyan is kellene újrafelhasználható és eltérő adatokkal is működő webes felületi teszteket létrehozni!


Selenium bevezető

A Selenium, Black Box jellegű integrációs felületi tesztek készítésére ad lehetőséget, ami annyit jelent, hogy az alkalmazás egészét teszteljük anélkül hogy ismernénk annak belső működését. A tesztesetek felvétele a Selenium IDE Firefox plugin segítségével történik, amely automatikusan rögzíti a böngészőben végrehajtott műveleteket. A Selenium parancsokat tartalmazó szkript többek között java forrásként is exportálható, amit némi refaktorálás és adatforrás bekötés után, eltérő környezetekben is használható tesztekké alakíthatunk!


Selenium fogalmak ismertetése

A Selenium elnevezéseit nem mindenhol használják jól, így kezdjük az alapfogalmak áttekintésével:
  • Selenium: A projekt neve.
  • Selenium 1: A Selenium projekt első verziója.
  • Selenium 2: A Selenium projekt második verziója.
  • Selenium Remote Control (RC): A Selenium 1 által használt API.
  • Selenium WebDriver: A Selenium 2 által használt alapértelmezett API.

A következő néhány Selenium-os bejegyzésemben a Selenium projekt 2-es verzióját fogom ismertetni a Selenium Remote Control API használatával, mégpedig azért mert a cikk írásakor a Selenium IDE még nem volt képes a WebDriver API parancsainak problémamentes exportálására. Szerencsére a fejlesztés jó ütemben halad, így már erre sem kell sokáig várni...


Selenium architektúrák rövid áttekintése

A Selenium RC parancsok visszajátszásához szükséges egy Selenium RC Server indítása, amely proxy-ként továbbítja a végrehajtandó műveleteket a böngészőnek. Selenium WebDriver API használatánál erre már nincs szükség, mivel a parancsok - egy gépes környezetben - közvetlenül a böngészőhöz továbbítódnak.



Selenium 2 újdonságai

A Selenium 2 legfőbb újdonságait a WebDriver projekttel történő egyesülés hozta.

  • Letisztultabb API, amit a WebDriver project biztosít. A Selenium RC parancsok Selenium 2-es környezetben való futtatásához a WebDriverBackedSelenium osztályt használhatjuk fel.
  • Beépített IPhone és az Android emulátor, melyek használatával mobil eszközök böngészőjében is futtathatók a tesztek.
  • Pop-up dialógusok bevonása a tesztesetekbe: basic authentication, fájl feltöltés pop-up dialógus.
  • Cross Domain Testing: Tipikusan a Facebook Connect-et használó alkalmazások tesztelése.
  • Amíg a Selenium 1 JavaScript-et használ a böngészőben történő tevékenységek végrehajtásához, addig a Selenium 2 - a WebDriver API felhasználásával – operációs rendszer szintű eseményekkel dolgozik a visszajátszás során, így standalone környezetben nem szükséges a Selenium Proxy Server indítása.

A Selenium lehetőséget ad arra, hogy eltérő böngészők különböző verzióival is letesztelhessük az alkalmazást. Böngésző nélküli tesztelésre is van mód a HtmlUnitDriver használatával, amely gyors teszt futtatásra ad lehetőséget, azonban a JavaScript motorja nem bizonyult megfelelőnek az összetettebb JavaScript könyvtárak használatakor (pl.: Prototype). A probléma valószínűleg a későbbi verziókban javításra kerül. A Selenium által támogatott platformok részletes listája megtalálható itt.


A Selenium-os teszteléshez szükséges eszközök

  • Szükséges böngésző a Selenium IDE kiegészítő használatához.
  • Firefox 3.6 vagy Firefox 4 is telepíthető, azonban későbbi Firefox verziókhoz a cikk írásakor még nem volt stabil verziója az összes felhasznált kiegészítőnek.
  • Firefox plugin, ami lehetővé teszi a böngészőben végrehajtott műveletek (kattintás, gépelés) felvételét, visszajátszását és exportálását. Érdemes a legfrissebb verziót telepíteni!
  • Selenium IDE kiegészítő, amely a rögzített Selenium tesztekhez ad javítási lehetőségeket ill. tippeket.
  • Selenium IDE kiegészítő, amely lehetővé teszi a rögzítet Selenium szkriptek WebDriver alapú exportálását.
  • Selenium IDE kiegészítő, amely a kiválasztott Selenium parancshoz tartozó html elemre, eltérő színezéssel rámutat a böngészőben.
  • Selenium 2 kliens oldali java könyvtár. A dokumentum írásakor a Selenium 2 rc3 (selenium-java-2.0rc3.zip) verziója már probléma nélkül használható volt.


Selenium IDE

A Selenium IDE kiegészítőt, telepítés után a Firefox/Tools/Selenium IDE menü kiválasztásával jeleníthetjük meg. A tesztek rögzítésének indítása ill. leállítása a jobb-fenti piros gombra kattintással történik. Indítás után a böngészőben végrehajtott műveletek (pl.: klikkelés, gépelés) azonnal megjelennek jobb oldali panel table tab-ja alatt. A rögzítés után a teszteset visszajátszható a ’play current test case’ gombra kattintva, melynek a naplója a Log tab-on tekinthető meg. A visszajátszás után a sikeresen végrehajtott parancsok zöld színnel, a végre nem hajtott parancsok pedig piros színnel jelennek meg. A Reference tab-ra klikkelve a táblázatban kiválasztott parancs használatáról és argumentumairól olvashatunk információkat, az Expert tabon pedig tippek és javítási ötletek jelennek meg, amelyeket azonnal elvégezhetünk az inspect, majd fix gombra kattintással.


Ha a toolbar-on bekapcsoljuk a ’HighLight Elements’ opciót, akkor a teszteset visszajátszása során az aktuálisan végrehajtott parancshoz tartozó html elem sárga háttérrel kivillan az oldalon. Hasonlóan, ha a táblázatban kiválasztunk egy sort és ráklikkelünk a target mező melletti Find gombra, akkor a böngészőben a lokátorhoz tartozó mező háttérszíne megváltozik. Ha fordított működésre lenne szükségünk, akkor használjuk az XPathFinder Firefox plugin-t, amivel a böngészőben megjelenő html elemekhez kaphatjuk meg a hozzájuk tartozó XPath kifejezést. Az elkészített tesztesetet a File/Export Test Case As… menüpont alatt többféle formátumban is exportálhatjuk.


Zárszó

A mostani bejegyzésemet leginkább egy bevezető jellegű kedvcsinálónak szántam, remélem elérte a célját! A folytatásban a Selenium parancsok használatáról fogok írni, de addig is egy kis olvasnivaló:


2011. augusztus 6., szombat

FitNesse tesztek indítási lehetőségei

Egy korábbi bejegyzésemben a FitNesse használatát mutattam be egy példán keresztül, a mostani cikkben pedig az elkészített teszt futtatási lehetőségeit fogom ismertetni.

A tesztek indítási módjaiban közös, hogy meg kell adni a típust (suite vagy teszt) valamint az útvonallal specifikált nevet, majd opcionálisan a végrehajtás eredményének kimeneti formátumát és a Fitnesse web-szerver által használt portot.

FitNesse tesztek wiki oldalról történő indítása

A tesztek indításának talán legegyszerűbb módja a wiki oldalról történő futtatás. A FitNesse web-szervert, a FitNesse gyökér könyvtárából indítsuk el a java -jar fitnesse.jar –p 6666 paranccsal, majd keressük ki a futtatni kívánt tesztet vagy suite-ot és kattintsunk a baloldali menüben a suite ill. test gombra. 

Indítás után, a típusnak megfelelően megjelenik egy ?test ill. ?suite paraméter az url-ben, melyek begépelésével a futtatás direkt módon is végrehajtható. Ha a tesztek eredményeit nem a wiki oldalon keresztül, hanem egy meghatározott formátumban szeretnénk elérni, használjuk a format url paramétert a text, xml vagy html értékekkel. pl.: ExampleTestSuite?suite&format=xml

A böngésző címsorában található context-root utáni URL-t, mint útvonallal specifikált nevet használhatjuk fel a suite-hoz tartozó összes teszt (ExampleTestSuite) vagy egy kiválasztott teszt (ExampleTestSuite.ExampleTest) futtatásához a további eseteknél.

FitNesse tesztek indítása parancssorból

A konzolról történő indításhoz a FitNesse gyökérkönyvtárából adjuk ki a java -jar fitnesse.jar -p 6666 -c "ExampleTestSuite?suite&format=xml" -d "/usr/local/Fitnesse" parancsot. A -c opcióval definiálhatjuk a futtatni kívánt tesztet és a kimeneti formátumot, a -d kapcsolóval pedig a munka könyvtárat állíthatjuk be.

FitNesse tesztek indítása Java kódból

A Fitnesse tesztek java kódból történő indításához, az Eclipse/Run Configurations menü alatt a java kódhoz tartozó konfiguráció Arguments tabján, a Working directory résznél adjuk meg a FitNesse munka könyvtár helyét.


A fejlesztőeszköz beállítása után, a tesztek Java kódból történő indításához az alábbi kódrészlet használható fel.
import fitnesse.Arguments;
import fitnesseMain.FitNesseMain
    
    ...
    
    public void testerMethod() throws Exception {
        Arguments arguments = new Arguments();
        arguments.setInstallOnly(false);
        arguments.setOmitUpdates(true);
        arguments.setPort("6666");
        arguments.setCommand("ExampleTestSuite.ExampleTest?test");
        arguments.setRootPath("/usr/local/Fitnesse");
        FitNesseMain.dontExitAfterSingleCommand = true;
        FitNesseMain.launchFitNesse(arguments);
    }
FitNesse tesztek indítása Ant használatával

Az Ant-tal történő futtatást akkor érdemes választani, amikor a FitNesse tesztek indítását a build folyamat részeként, automatizáltan akarjuk végrehajtani. Az alábbi kódrészleten egy ant build fájl tartalma látható, ahol is a start target végzi el a kiválasztott tesztek indítását.
<project basedir="/usr/local/Fitnesse" default="start" name="Test">  
 <path id="classpath">
  <fileset dir="/usr/local/Fitnesse" includes="**/*.jar">
  </fileset>
 </path>   

 <target name="start">        
  <java failonerror="true" fork="true" jar="/usr/local/Fitnesse/fitnesse.jar">       
   <arg value="-p"></arg>      
   <arg value="6666"></arg>
   <arg value="-c"></arg>                
   <arg value="ExampleTestSuite?suite&format=xml"></arg>     
  </java>
 </target> 
</project>
FitNesse tesztek indítása a Hudson CI szerver segítségével

A Hudson ill. Jenkins Continuous Integration szerverek szintén lehetőséget nyújtanak a FitNesse tesztek futtatásához. A folyamatos integráció lényege, hogy a fejlesztők folyamatosan integrálják a munkájukat egy központi szerveren, ahol is a build folyamat után ellenőrzéseknek vetik alá a szoftvert mint terméket. Ilyen ellenőrzések lehetnek a statikus kód ellenőrzések, teszt lefedettség vizsgálatok vagy a FitNesse tesztek futtatása. Az ellenőrzések eredményét a Hudson képes megjeleníteni, így folyamatosan követhetjük a projekt állapotát és szükség szerint időben beavatkozhatunk!

Itt jegyezném meg, hogy a Hudson/Jenkins az egyik kedvenc eszközöm, ezért hamarosan a folyamatos integráció témakörben is fogok blogolni... :)

A rövid áttekintés után térjünk vissza FitNesse tesztek indításához. A Hudson plugin manager oldalon telepítsük fel a FitNesse plugint, majd hozzunk létre egy új jobot a projektnek. A build résznél válasszuk ki az ”Execute Fitnesse tests” opciót, majd a ”Start new Fitnesse instance as part of build”-et bejelölve töltsük ki a mezőket az alábbi ábra szerint:

 
Ahhoz, hogy a FitNesse tesztek lefutási eredményét riportként is megtekinthessük, állítsuk be a FitNesse result fájlt, a ”Post build action” résznél is. Figyeljünk arra, hogy az eredmény fájl útvonalát a Hudson workspace-en belül adjuk meg!


Mentsük el a beállításokat és futtassuk a létrehozott job-ot, majd tekintsük meg a helyes beállításokról tanúskodó konzol naplót.


A FitNesse teszteket sokféleképpen, de mégis hasonló séma alapján indíthatjuk, így megvan arra a lehetőségünk, hogy mindig a felmerült igényekhez legjobban illeszkedő módszert válasszuk!

A következő cikkemben még mindig a tesztelési témakörnél maradok, de már egy másik remek teszteszköz ismertetésére fogok rátérni!

2011. július 25., hétfő

IBM 48 órás programozó bajnokság - Ne maradj le!

Idén is megrendezésre kerül az IBM 48 órás programozói verseny melynek célja, hogy a hazai felsőoktatásban résztvevő tehetséges hallgatók - csapatmunka keretében - összemérjék Java EE szoftverfejlesztői tudásukat, IBM-es technológiák felhasználásával. A jól teljesítő diákok álláslehetőséggel és értékes nyereményekkel lehetnek gazdagabbak, melyet az IBM Magyarország és partnerei biztosítanak. A versennyel kapcsolatos további információk megtalálhatók hivatalos honlapon.

Mielőtt megosztanék pár hasznos tanácsot a versennyel kapcsolatosan, következzen egy személyes vonatkozású áttekintés a 2007-es és 2009-es IBM 48 versenyről.

Először 2007. novemberében vettem részt az IBM 48 versenyen. Akkoriban kezdtem el ismerkedni a Java Enterprise világgal, az Imre Gábor által tartott Szoftverfejlesztés J2EE platformon választható tárgy keretében. Úgy emlékszem valamelyik óra végén szólt, hogy van ez a jó kis verseny és szívesen venné ha indulnánk rajta. Bár a csapatot (BBS) csak a verseny előtti napokban sikerült összeállítanunk és túl sokat nem is tudtunk rá készülni, különdíjasok lettünk.











A 2009. novemberi versenyre - a már meglévő tapasztalatokra építve - egy hétvégét készültünk, ahol is egy korábbi feladatot oldottunk meg, átnéztük az IBM technológiák használatát és megbeszéltük hogy ki mivel fog foglalkozni. A JBoss Seam keretrendszert választottuk a fejlesztéshez, melynek a WebSphere AS 6.1-el való integrációjáért én voltam a felelős. Bár a bekonfigurálás igénybe vett egy kis időt, végül mindannyian úgy láttuk hogy megérte a JBoss Seam-et választani, ugyanis nagyban felgyorsította a fejlesztés menetét és a további JBoss-os technológiák használatát (JBoss RichFaces, JBoss Rules) is megkönnyítette. A 2009-es csapatommal (SeamTeam) második helyezést értünk el, aminek nagyon örültünk! Az előző bajnoksághoz hasonlóan a verseny és a díjkiosztó is nagyon jó hangulatban telt el, ezúton is köszönet a szervezőknek!











Végül következzenek a beígért tippek, melyeket érdemes megfogadni a csapatoknak:

  • Készítsetek el legalább egy, de inkább két régebbi verseny feladatot, így még a verseny előtt kibuknak a nehézségek és a csapat is jól összeszokik!
  • Ismerjétek meg az IBM-es technológiák alapszintű használatát, mert a versenyen erre biztos nem lesz idő!
  • Olyan webes keretrendszert használjatok, amit már jól ismertek. Nem szerencsés amikor a versenyen derülnek ki a keretrendszer rejtett hibái.
  • A fejlesztéssel párhuzamosan, folyamatosan dokumentáljatok. Nem kell túlzásba vinni, de a fontosabb döntésekhez legyenek ábrák, diagramok és egy pár soros magyarázó szöveg. 
  • Az értékelés során az én meglátásom szerint, fontosabb szempont volt az hogy ki meddig jutott el, mint a kód minősége vagy éppen a felhasznált technológiák sokrétűsége. Továbbá nem javaslom, hogy tesztek készítésével töltsétek az időt, - annak ellenére hogy a csapból is a TDD folyik - mert a feladatot nem fogjátok tudni befejezni.
  • Nem érdemes apróbb dolgokon szöszölni, ha problémába futottatok inkább hagyjátok ki, majd a verseny vége fele vegyétek elő újra.
  • A feladat bemutatása előtt mindenféleképpen klikkeljétek végig a művet, mert elég rosszul néz ki ilyenkor egy nemvárt kivétel.
  • Java EE alapú webalkalmazást készítsetek, AJAX-os keretrendszer felhasználásával, mert a zsűri nem desktop Java alkalmazásokat vár.
  • Használjátok ki, hogy a versenyre bármilyen saját kódot bevihettek. Érdemes előre elkészíteni egy alap projektet ami tartalmaz egy bejelentkeztető oldalt és egy felhasználó management modult, mert ezek szinte minden versenyen előfordulnak!
  • Nem mindig volt előre telepített SCM, így készüljetek arra, hogy lehet nektek kell majd összerakni és beállítani egy CVS vagy SVN szervert.
  • Ha van rá lehetőségetek, vigyetek saját laptopot amin már be vannak állítva a fejlesztéshez szükséges eszközök!
  • A verseny előtti napon aludjátok ki magatokat, de a verseny alatt is legalább 4-5 óra alvást iktassatok be mindkét napon! :)

Remélem hasznos információkat tartalmazott a bejegyzés. Jó versenyzést kívánok mindenkinek!

2011. július 19., kedd

FitNesse, a wiki alapú tesztkeretrendszer

A FitNesse egy acceptance tesztelési keretrendszer, melynek célja hogy a teszteket készítő fejlesztők és a területi szakértők közti szakadékot megszüntesse, azáltal hogy a szakértők számára is elérhetővé teszi a tesztek kezelését és végrehajtását.

Működésileg egy saját web-szerveren futó wiki site, amely strukturált és könnyen olvasható formában teszi elérhetővé a teszteket. A teszteket wikis formázással megadott táblázatok reprezentálják, melyekhez egy-egy Fixture java osztály tartozik. A Fixture osztályok teremtik meg a kapcsolatot a wikis táblázatok és a java tesztkód között. A táblázatok input adatokat és a várt output adatot tartalmazzák, valamint a Fixture kód elérhetőségét. A FitNesse egyszerű felépítése és koncepciói nagyban hozzájárulnak a kiváló használhatóságához, melyek lehetővé teszik széleskörű alkalmazását!

FitNesse telepítése és a menüstruktúra ismertetése

A FitNesse telepítése és indítása nagyon egyszerű! Töltsük le a legfrisebb fitnesse.jar-t a FitNesse hivatalos oldaláról, majd másoljuk be abba a könyvtárba ahova telepíteni szeretnénk, végül adjuk ki a java -jar fitnesse.jar -p 6666 parancsot. Amennyiben a 80-as port már foglalt, a –p opcióval jelölhetünk ki egy szabad portot. A parancs futtatása után létrejön a FitNesseRoot nevű könyvtár a sablon wiki oldalakkal feltöltve, melyet a http://localhost:6666/ címen tekinthetünk meg.


Rövid áttekintés a baloldali menük funkcióiról:
  • Edit: Az aktuális - jobb oldalon megjelenő - wiki oldal tartalmának szerkesztése.
  • Properties: Az aktuális wiki oldal tulajdonságainak szerkesztése.
    • Page Type: Az oldal típusának meghatározása, ami lehet egyszerű wiki oldal, teszt oldal vagy suite oldal.
    • Actions: A baloldalt megjelenő akció funkcionalitású menüelemek meghatározása.
    • Navigation: A baloldalt megjelenő navigációt biztosító menüelemek kiválasztása.
    • Securtiy: Biztonsági megszorítások az aktuális wiki oldalra vonatkozólag.
  • Refactor: Az aktuális wiki oldal törlése, átnevezése vagy új helyre mozgatása.
  • Where Used: Az aktuális wiki oldal más oldalakon történő hivatkozásait mutatja.
  • Search: Kifejezés keresése a wiki oldalakon.
  • Files: A fájlrendszerbeli könyvtárstruktúra áttekintése és böngészése.
  • Versions: Az aktuális wiki oldal módosítás utáni verzióinak megtekintése és visszaállási lehetőség egy korábbi verzióra. (rollback)
  • Recent Changes: A legutóbbi változások teljes wiki struktúrát tekintve.
  • User Guide: Általános ismertető a FitNesse keretrendszerről.
  • Test History: Az elindított tesztek sikerességéről kaphatunk információkat.
  • Suite: A suite oldalak esetén megjelenő menüelem, amivel a suite alatt lévő teszteseteket futtathatjuk a wiki oldalon keresztül.
  • Test: Az adott oldalhoz tartozó teszteset futtatása a wiki oldalról indítva.

FitNesse Suite létrehozása és konfigurálása

Az ExampleTestSuite oldal létrehozásához kattintsunk a cím melletti ”add child” linkre majd adjuk meg az oldal típusát és nevét, végül kattintsunk az add gombra.


Ahhoz hogy a felvett oldalt használni tudjuk válasszuk ki az Edit menüt, majd a megjelenő wiki szerkesztő felületen írjuk be az oldal nevét, esetleg töröljük a számunkra szükségtelen részeket az oldalról.


A mentés után megjelenő oldalon az ExampleTestSuite[?]-re kattintva létrehozhatjuk magát a wiki oldalt. Ha az újonnan létrehozott oldal baloldali menüjében nem jelent volna meg a Suite menüelem, akkor a Properties menü alatt módosítsuk a megfelelőre.


Az ExampleTestSuite oldalon kerül definiálásra a classpath valamint két Fitnesse változó a define kulcsszó megadásával. A classpath sorokkal a szükséges könyvtárak és a Fixture kódot tartalmazó class ill. jar helyét adhatjuk meg, a define kulcsszóval pedig a teszteseteknél is használni kívánt változókat definiálhatjuk. Azáltal, hogy a classpath és a define értékeket az ExampleTestSuite-nál adjuk meg, a hierarchiában alatta lévő suite-okra és tesztekre (pl.: ExampleTest) is tovább öröklődnek, bár az alsóbb szinteken szükség szerint felüldefiniálhatók.



FitNesse teszt létrehozása és futtatása

A következőkben egy egyszerű String összefűzési funkció tesztelését fogom ismertetni a ColumnFixture felhasználásával.

Az ExampleTestSuite véglegesítése után, a suite alá vegyünk fel egy ExampleTest oldalt az ”add child” linkre kattintással úgy hogy az oldal típusa test legyen, majd hozzuk létre a tesztesethez tartozó táblázatot. A táblázat első sorában definiáljuk a Fixture osztályt (MyFitnesseTest), valamint a felhasználni kívánt változókat melyek a Fixture osztályban a getArgs() metódussal érhetők el. A táblázat második sora az oszlop neveket tartalmazza (username, password), melyeket a Fixture osztályban publikus mezőként kell megadni, valamint a tesztmetódust. (testConcatenation). A táblázat további sorai a bemeneti adatokat és a kimenet elvárt eredményeit tartalmazzák, melyek egy-egy teszt lefutásnak felelnek meg.

 


A teszthez tartozó Fixture osztály kódja az alábbi forráskódrészleten látható:
package fitnesse.example;

import fit.ColumnFixture;

public class MyFitnesseTest extends ColumnFixture{

    public String username;
    public String password;
    
    public String testConcatenation(){
        
        System.out.println(username);
        System.out.println(password);
        System.out.println("myVariable1: "+getArgs()[0]);
        System.out.println("myVariable2: "+getArgs()[1]);
        
        return new StringBuilder()
                    .append(username)
                    .append(password)
                    .append(getArgs()[0])
                    .append(getArgs()[1]).toString();
    }
}

Az elkészült tesztet az ExampleTestSuite oldalon látható suite gombra klikkelve vagy az ExampleTest oldal test gombjára kattintva indíthatjuk el, melynek eredményét egyből megtekinthetjük az oldalon.


FitNesse oldalstruktúra kialakítása

A tesztek végrehajtását teszt oldalanként vagy suite-onként van lehetőségünk elindítani. Egy suite, teszteket és további suite-okat tartalmazhat. A wiki oldal struktúrát úgy érdemes kialakítani, hogy készítünk egy gyökér suite oldalt ahova további suite oldalakat veszünk fel, majd az alsóbb szintekre helyezzük a tesztoldalakat. Ez a kialakítás azért hasznos, mert a suite-ok csoportba fogják a teszteket így a tesztek futtatását is ezen csoportok alapján hajthatjuk végre. A suite-os csoportosítás további előnye, hogy a classpath ill. változó deklarációk is öröklődnek a hierarchiában lentebb található suite-okra és tesztekre, így azokat elég csak a felsőbb szinten megadni. Persze egy alsóbb szinten deklarált változó mindig erősebb hatókörrel bír, így névegyezés esetén az alsóbb szintű fog érvényre jutni.


Zárszó

A wiki-s táblázat alapú tesztek az olvashatóságot és a karbantarthatóságot tekintve is előnyösebbek a programozott tesztekkel szemben, igaz a kiegészítő kódok elkészítésére időt kell szánni.

A cikk folytatása hamarosan következik...!

2011. június 20., hétfő

JRebel: Gyorsítsd fel a fejlesztést!

Enterprise Java alkalmazások fejlesztésénél az egyik legnagyobb problémát a hosszú telepítési idő okozza. A Java osztályokon - legyen akár EJB vagy JPA entitás - végrehajtott módosítások után, ha látni szeretnénk a változtatások eredményét, újra kell buildelni és telepíteni a teljes alkalmazást, ami sok időt vehet igénybe a fejlesztés folyamatából.

A JRebel egy JVM-be épülő plugin, ami segít elkerülni a folyamatos alkalmazás újratelepítést, azáltal hogy az IDE-ben végrehajtott módosításokat azonnal elérhetővé teszi a telepített alkalmazáson.

Működését tekintve a java osztálybetöltőt terjeszti ki, hogy képes legyen kezelni és újratölteni a megváltozott osztályokat. A betöltendő osztályok keresésénél a classpath-t (beleértve a WEB-INF/classes könyvtárat) és a rebel.xml fájlban meghatározott helyeket figyeli, a módosult osztályok meghatározására pedig a fájlrendszerbeli időbélyeget használja fel. Ahhoz, hogy a JRebel biztosítani tudja az alkalmazás folyamatos működését, az osztályok újratöltése után a régi osztály példányokat is megőrzi. Ez viszont azzal jár, hogy egy újonnan felvett példány változó nem lesz a konstruktor által inicializálva a meglévő példányoknál, mivel a konstruktor nem kerül ismételten meghívásra.

A JRebel támogatja a főbb alkalmazásszervereket (JBoss, WebSphere, GlassFish, Weblogic) és az Apache Tomcat szervlet konténert is. A képességei kiterjednek a mezők, metódusok, annotációk, ősosztályok, interfészek, EJB-k, JPA entitások, JSP, JSF, XML és property fájlok módosítására, felvételére és törlésére valamint jópár keretrendszer (JBoss Seam, Spring, Vaadin, GWT, Struts, Wicket stb...) támogatására is. A teljes listáról a feature comparison matrix oldalon található bővebb leírás.

A telepítéshez és a fejlesztőkörnyezethez történő beállításhoz egy nagyon jó dokumentáció és több tutorial is tartozik! A konfigurálást követően érdemes lecserélni a rebel.xml fájlban található lokális projekt elérési útvonalat egy custom JRebel propertyre, így a fejlesztő csapat minden tagja a saját környezetének megfelelően állíthatja be ezen értéket.

Jómagam már 2009 decembere óta több projektben is használtam a JRebel-t és kiválóan működik! Nálam kiérdemelte a legjobb Java tool díjat. :) 

Ne hagyd ki ezt a cikket sem!