Monday, May 30, 2011

Agilis tesztelés, a kezdetek


Az agilis fejlesztés és vele együtt a tesztelés teljesen más szemléletet kíván meg. A tesztelés is ugyanolyan ciklusban működik, mint a fejlesztők esetén, nincs is különbség, egy csapatba kell hogy tartozzanak. Ugyanis a teszteknek és tesztelőknek ugyanolyan rugalmasnak kell lennie, mint a fejlesztőknek a fejlesztés során. A “whole team” szemlélet megköveteli, hogy a csapat egy-egy tagja ne csak egy területen legyen specialista, hanem legyen általánosságban véve specialista. Így a csapatból többen fognak érteni a programozáshoz, az adatbázis tervezéshez, de akár a teszteléshez is. Ezáltal a folyamatos kis lépésekben történő fejlesztés része lesz a tesztelés is.

A tesztelők szempontjából megkülönböztethetünk két szemléletet. A teljesen független és a párhuzamosan független tesztelést. A teljesen független tesztelést a tesztelők a user story-k alapján végzik el. Kérdéses, hogy mennyire hatékony ez a módszer ugyanis sokszor nem elegendő a story-k ismerete ahhoz, hogy újrafelhasználható teszteket írjanak. Ehelyett inkább előnyben kéne részesíteni a párhuzamosan független tesztelést, amelynél a tesztelő az agilis csapathoz szorosabban fog tartozni és az adott rész tesztelését akár egy fejlesztővel együtt végzik. Ilyenkor a tesztelő jobban átlátja a tesztelendő rendszer működését és könnyebben ír hatékony teszteket, a fejlesztő segítségével pedig újrahasználható formában tudják megvalósítani.

Agilis tesztelés követelményei:
- Gyors tesztelési automatizálás, nagy fokú rugalmasság a változásoknál.
- Gyors visszajelzés a megfelelő szemlélettel rendelkező fejlesztők felé.
- Csökkentenie kell a kiadásokat a gyors és hatékony hibakeresés segítségével.
- Időben kiadott, magas minőségű rendszert kell biztosítania.

Az első szint, ami a fejlesztőket érint az a Test Driven Development ( TDD ). Külső függőségek nélküli kódhoz kell kis lépésekben teszteket írniuk, hogy a fejlesztés és legfőképpen a tervezés kis, folyamatos és biztonságos lépésekben történhessen. A gyakorlat azt mutatja, hogy sokan nem képesek a TDD által megkívánt fegyelemre (például betartani, hogy először a tesztesetet írják meg és utána a kódot), ezáltal a TDD-től eltérően a kód elkészülte után egyből írják meg a tesztet. Ez is jobb, mintha pár héttel később írnák meg a tesztet. A kódolás utáni teszt írás egyik oka lehet például a tesztek általi kódlefedettséget mérő eszközök, amelyek jelzik, hogy a kód hány százalékát érint teszt, és egyes fejlesztőket az motiválja, ha elérik a 90% feletti értéket. Ez a szemlélet csak a kód érvényesítését szolgálja.

A következő szint az integration teszt. Agilis környezetben nehéz feladat, arra kell törekedni, hogy a tesztelők és a fejlesztők megtalálják annak a módját, hogy minél több újrahasználható tesztet tudjanak írni.

Agilis integration teszt lépései:
- Azonosítani kell az egyes szoftverelemeket.
- Újrahasználható, környezetfüggetlen teszt eseteket kell kitalálni.
- A még nem automatizálható részekhez szimulátorokat kell írni.
- Illesztők elkészítése    a tesztet végrehajtó modulba, hogy kezeljék a szimulátorokat vagy közvetlenül az egyes szoftvermodulokat.

Fontos, hogy használjunk illesztőket agilis tesztelés esetén. A tesztesetek megfogalmazásánál úgy járjunk el, hogy csak az ügyfél szempontjából fontos paramétereket tartalmazza. Például, ha egy API-t akarunk tesztelni, akkor adjuk meg a kívánt lekérdezés paramétereit ügyfél szempontból és írjunk hozzá egy illesztőt, ami elvégzi a REST-es hívásokat. A technikai háttér megváltozásakor, például REST helyett SOAP lesz, csak egy új SOAP illesztőt kell írni a tesztekhez, ezáltal a legtöbb teszteset ugyanúgy használható lesz.

Fontos, hogy az agilis tesztelést, csak fokozatosan kéne bevezetni, ha egyszerre akarunk mindent bevezetni, akkor sehova nem jutunk. Lépésről lépésre kéne kipróbálni az egyes módszereket, ami hatékony azt megtartani, ami nem azt elvetni.

Tesztelés, a kezdetek

A tesztelés a szoftverminőség-biztosítás fontos eleme, melynek lényege: a program végrehajtásának a folyamata azzal a szándékkal, hogy megtalálja a hibákat. Meg kell tanulni azt a szemléletet, hogy a tesztelés csak akkor sikeres, ha találunk hibákat. Az egyik nehéz feladat ezzel kapcsolatban, hogy a fejlesztők is sikerként kezeljék a kiadás előtt jelentkező problémákat. A klasszikus szoftvertesztelés egy már véglegesnek szánt specifikáció alapján történik. A teljesen független tesztelő csapat a leírások alapján tervezi meg a tesztelés módját és eseteit, majd megírják őket és futtatják; ha a tervezés és megírás költségeit fedezi a megtalált hibák kiadás előtti javításával nyert költségmegtakarítás, akkor tekinthető sikeresnek a tesztelés gazdasági szempontból; ha nem képzett tesztelők végzik ezt a munkát, akkor előfordulhat, hogy nincs meg a kellő motiváció és tudás ahhoz, hogy megfelelően működjön a tesztelés. Általában ilyenkor nincs mérés a tesztelés eredményéről, valamint nem a megfelelő elvek mentén halad.

A tesztelés során akkor vagyunk sikeresek, ha találunk hibát. Ha pedig minden teszt jól lefutott de egy fő funkcióban még is van hiba, akkor bizony a tesztelésünk semmit sem ért. Az is sikertelen tesztnek számít, ha írunk egy tesztesetet, ami nem talál hibát. Ne hagyjuk, hogy hamis biztonságérzetet keltsen bennünk az, hogy a tesztek hiba nélkül futottak le, ugyanis az csak azt jelenti hogy néhány meghatározott paraméterrel jól működik a programunk.

Az emberek gyengébben teljesítenek, ha az adott feladatot lehetetlennek vagy nehezen megvalósíthatónak tartják. Megfelelő teszteseteket kell definiálni, amik könnyen megvalósíthatóak kellenek hogy legyenek. A tesztelés inkább romboló tevékenység.

Tesztelési szintekre példa:

module
unit: white box tesztelés részeként tekinthető. A fejlesztő végzi, és külső függőségek nélküli funkcionalitást tesztelnek vele.

integration: Az egyes szoftverelemek kombinálva vannak tesztelve együtt, mint egy csoport. A különböző fejlesztők által írt modulok közti hibát kéne felderítenie.

system:
system testing: A teljesen elkészített rendszer tesztelése abból a szempontból, hogy megfelele-e a követelményeknek.   

system integration testing: A külső alkalmazások bevonásával végzett tesztelés.

acceptance:   
user: A fejlesztői oldalról végzett ellenőrző tesztelés.

automated: Ügyfél tevékenységének szimulálását végző automata eszköz általi tesztelés. Összekeverhető az automatizált integration tesztekkel, a különbség a kettő között, hogy az acceptance teszt rendszer független ( black box ) szempontból, az integration teszt pedig a rendszer alapos ismeretét felhasználva ( white box ) végzi a tesztelést.

contract: A szerződés alapján végzett ellenőrző tesztelés, például egyes elemek megfelelnek-e valamilyen törvény által előírt feltételnek.

alpha: Fejlesztői oldalról végzett kiadás előtti tesztelés.

beta: Ügyfél oldalról végzet tesztelés.

Tuesday, May 24, 2011

Test Driven Development, a kezdetek

Sok esetben alapos szemléletváltozás szükséges ahhoz, hogy a tesztvezérelt fejlesztést előnyeit ki tudja használni a fejlesztő. Legalább annyi támogatója van, mint ellenzője. Sajnos feltételezésekbe kell bocsátkozzak, de szerintem az ellenzők egy jó része nem értette meg mire is való a TDD, és sokszor a lelkes kezdők is hasonló sorsra jutnak. A TDD kulcsszava a tervezés nem pedig a tesztelés. Járjuk körül egy kicsit, hogy miért is létezik ez az egész, hogy végül (reményeim szerint) használható alapot adjak azok kezébe, akik megpróbálkoznak a TDD-vel. Ez természetesen azzal jár, hogy néhány vitatott kérdésben elkötelezzem magam valamelyik oldal mellett.

Jól működő, könnyen bővíthető alkalmazás, szolgáltatás, stb. kell ahhoz, hogy a cég tulajdonosai élvezhessék a szoftverfejlesztés által megvalósuló ( jól megérdemelt ) hasznot. Ehhez biztosítani kell a létrehozott szoftver minőségét. Egyik ilyen módszer a tesztvezérlet fejlesztés. Minek és milyen minőségét fogja biztosítani az szerepeljen a következő sorokban, de előtte még oszlassunk el néhány tévhitet.

Bizonyos szabályokat be kell vezetni ahhoz, hogy arra használjuk a TDD-t amire kell. A TDD egy fejlesztési módszer, nem pedig a szoftver tesztelésére használható eszköz. A módszer alkalmazásához teszteket is kell írnunk, azok pedig egység teszek (Unit test) lesznek. Mindenhol futtatható kell hogy legyen. Munkahelyi gépeden, otthoni gépeden, vonaton, Hősök terén. Bármilyen sorrendben lehessen futtatni a különböző teszteket. Külső függőségek nélkül, mint  például hálózat, vagy fájlrendszer. A teszteseteknek csak akkor kéne változniuk, ha a kódnak is változnia kell. Nem minden rendszer esetében használható ez a módszer.

A fejlesztési folyamat négy lépésre bontható:

Feladatok meghatározása:
     Ebben a lépésben az ügyfél igényeknek megfelelő funkcionalitást kell feladat meghatározássá alakítani teszt esetek formájában. Meg kell határozni, hogy mit kellene tennie a rendszernek. Fontos, hogy nem azt kell itt kitalálni, hogy az adott fejlesztő hogyan valósítsa meg az adott feladatot, hanem hogy mit kell majd megvalósítani. A mit meghatározása által a fejlesztő is jobban megérti a feladatot, kisebb a félreértés lehetősége. A teszteset megírása után következik a megvalósítás.

Megvalósítás:
     Ezután következik a hogyan valósítsuk meg az előre definiált feladatot. Azután meg is kell valósítani. A megvalósításnak kéne a legkönnyebb résznek lennie, ha mégsem könnyű akkor a következő problémák fordulhattak elő. Túl nagy feladatot határoztunk meg az előző lépésben, így az a feladat, hogy kisebb feladatokra bontsuk. Felmerül a kérdés, hogyan lehet könnyű a megvalósítás, ha előtte még egy libet is el kell előtte készíteni? Ilyenkor az adott lib feladatait kell meghatározni úgy, hogy a fejlesztők lesznek az ügyfelek és a ő igényeiket kell kielégíteni, és TDD alapján lefejleszteni.
     Ha nem nagy lépésről van szó de mégis nehéz megvalósítani, akkor refaktorálni kell az adott részt. Ez lehet azért mert koszos a kód, vagy nincs jól megtervezve, stb. (A refaktorálás viszont egy külön történet, melyet meghagyunk egyelőre más blogoknak.)

Ellenőrzés:
     A megfelelő eszközzel le kell ellenőrizni, hogy sikeresek lettek-e a tesztek. Ennek gyorsnak és könnyűnek kell lennie. (A tesztek nem futhatnak néhány mp-nél lassabban, akkor már nagy a gond, ha több idő kell.) Ezáltal folyamatosan ellenőrzött, tervezett és fejlesztett lesz a kódunk.

Tisztítás:
     Ha elértük, hogy sikeresen lefussanak a tesztek, akkor jön a kód tisztítása. A működő kódot átnézzük, a duplikációkat eltüntetjük. Beszédesebb neveket választhatunk a változóinknak. Mivel az ügyfél számára fontos funkcionalitás már le van tesztelve, ezért bátran megtehetjük mindezt, ugyanis ha például elgépeljük valahol az új változónevet, akkor rögtön jelez a teszt, hogy hiba van. Esetleg túl nagy lett a függvény, akkor több kisebb függvényre szétbonthatjuk, stb. Ettől tisztább és karbantarthatóbb lesz a kód.

Figyeljük meg, hogy az összes lépés megfeleltethető a tervezés egy-egy részének. A meghatározott feladatok automatizálásának köszönhetően lehetséges, hogy kis biztonságos lépésekben történjen a rendszer felépítése és tervezése. Segíti a feladat megértését, rákényszerít a könnyű megvalósíthatóságra a folyamatos tisztítás és újratervezés segítségével.


TDD előnyei:
- Refaktorálást segíti.
     - Bátrabban módosítjuk az elkészült kódot.
     - Könnyebb egy tesztelhető kódot refaktorálni ( a tervezés miatt ).
- Megmutatja, hogy hol hasalt el a kód.
- Az is segítség lehet, hogy hol nincs hiba.
- Segít tesztelhetővé tenni az alkalmazást.
- Rákényszerít, hogy egy függvény ne foglalkozzon sok mindennel.
- Csak olyan kódot írsz, ami a teszthez kell.
- Előre kell tervezni.
- Elvileg jobban is lesz ettől megtervezve. :)
- Gyors, folyamatos visszajezést kapsz a funkció állapotáról.
- Az utolsó változtatásodnál ha érintett egy régebben megírt függvény, akkor azt pontosan jelzi.
- Jobban ellenőrizhető a munka.
     - Van, hogy egy funkciónak nincs látható eredménye egy hétig. Ezzel szemben a TDD-nél naponta meg tudod mondani, hogy mennyi sikeres tesztet tudtál írni.
- Segít megérteni a feladatot a példákon keresztül.
- Időcsökkentő tényező a hibajavításnál és a refaktorálásnál.
- Hibajavításnál segíthet pontosabban megjelölni a hiba helyét.
- Akár dokumentációként is szolgálhat a teszt. Példakódnak tekinthető. ( Python doctest erre az elképzelésre épül. )
- Biztosítja, hogy az új kód nem érint más tesztelt egységet.
- Vannak emberek akiknek ez lelkisegély.
     - Örül, hogy de jó, sikeresen lefutott a teszt.
     - Bizonytalanoknak mentsvár. :)
     - Vannak akik ettől lesznek motiváltak és büszkék lesznek a munkájukra.
- Vitatkoznak arról, hogy melyik a hatékonyabb módszer, a TDD vagy a kód felülvizsgálat.
     - Szerintem a kettő együtt hatékony, egyik nem helyettesítheti a másikat.
- Ha nincs TDD, akkor gyorsabban kész lesz, de nehezebben módosítható később.
- A TDD tisztítás része pl. kódfelülvizsgálatnak is tekinthető.
- Stabilitást elősegítheti.

Hátrányok:

- Nő a fejlesztési idő. (Refaktornál csökken.)
     - Bevezetésekor drasztikusan.
- Ha nem tiszta, hogy mit kell tenni az adott feladattal könnyen írhatunk rossz tesztet, amit át kell majd írni. (Újabb időnövelő tényező.)
- Menet közben történt koncepcióváltásnál (létezik ilyen), könnyen kukába mehet az egész. (Újabb időnövelő tényező.)
- Sokan azt hiszik ettől hibamentes lesz a program működése.
- Nem csodafegyver. A rendszer tesztelésének (minőségbiztosításának) csak egy kis részét kéne, hogy képezze. (Accaptance test, integration test stb. mellett)
- A unit test csak annyit jelent, hogy önálló kis részek jól működnek.
- Csak tapasztalt fejlesztőkkel érdemes használni.
- A tervezést nem mindig lehet úgy alakítani, hogy az megfeleljen a TDD-nek.
     - Adatstruktúrák (közvetett módon) és "black box algoritmusok" tesztelésére jó.
     - Hálózattal, fájlrendszerrel kapcsolatos dolgok kilőve.
- Páros programozásban lenne érdemes használni.
     - A tesztek írása unalmas lehet egyesek számára. Nagy fegyelemre lenne szükség.
     - Nehéz belerázódni, ezért arra a következtetésre jutnak, hogy semmi értelme.
     - Nehezebben történik meg, hogy egy feladat megoldásának hevében megírod az összes feladatot egy teszt segítségével. :)
     - TDD-ben tapasztalt párral lenne a legideálisabb.
     - A páros programozás előnyeit pedig még nehezebben lehet megértetni a managerekkel. :)
- Nagy lelkesedés szükéséges az elején.
     - Ha írsz hat tonnányi tesztet, amiről kiderül, hogy semmit nem ér akkor biztosan szükség lesz rá.
- Nehéz megmagyarázni a managereknek, hogy az elején miért tart ennyi ideig a fejlesztés.

A tesztelés legyen veletek.

A szoftverfejlesztés avantgárdja

A tesztelés művészete a mai napig sötét szálként rejtőzik a szoftverfejlesztés intrikákkal teli birodalmában, ahogy azt Myers mester is kifejti. Hogyan fordulhat elő, hogy azok az irányzatok, amelyek  teljesen átformálták a fejlesztés folyamatát és minőségét, még mindig a súlyos hiányosságok halmazában lebeghetnek nemcsak a frissensült ifjú titán, de a megedzett, nagy csatákat megélt programozók elméjében is? Talán a következő okok is közrejátszhattak ennek a szomorú ténynek a kialakulásában:

- Nem foglalkoznak vele eleget a tanodákban.
- Nem érnek rá foglalkozni vele a cégeknél, mert az ötleteket kevés pénzből és időből szeretnék megvalósítani.
- Nem annyira izgalmas tesztelni, mint fejleszteni.
- Jobb azt hinni, hogy a programban nincsenek hibák, mint szembenézni velük és időt szánni a megkeresésükre.

Egye fene, nem számít, de talán ez a tesztelés körüli bloghajóút fényt termel a kedves publikum és jómagam új irányokra éhező lelkében.