Sunday, December 9, 2012

Second Global Day of Coderetreat

Nagyon jól éreztem magam az eseményen, annak ellenére, hogy az egész szombatot ott töltöttem, de aki programozónak gondolja magát annak ki kell próbálnia. Coderetreatnek az egyik legfontosabb eleme a gyakorlás. Kifejtették a szervezők az elején, hogy a festők a táncosok is addig gyakorolnak, amíg a részükké nem válik az adott mozdulatsor. Ami a megdöbbentő, hogy én ezt tudom. Elég sokat festek, rajzolok (az átlaghoz képest), és mestrem László Bandy nem győzi ismételni: minden nap egy vonal. Ami azt jelenti, addig kell gyakorolni, amíg eljutsz odáig, hogy ne a vonal húzásán gondolkozz amikor alkotsz (valamint ne lepődj meg azon, ha nem tudod miért nem sikerül oda húzni a vonalat ahova kell), hanem azon amit meg akarsz valósítani.

Személyiségemből adódóan az asszociációkból élek, de hogy nem sikerült felismernem, hogy ez a programozásnál sincs másképp, az azért megdöbbentő. Mivel ezt most személyesen tapasztaltam meg, ezért egyik legfontosabb feladatomnak kell hogy tartsam a gyakorlást, ha a szakmám "művésze" akarok lenni. Az egyik lelkes résztvevő még el is mondta, hogy ő bizony reggelente ezzel a "vonal húzásával" kezdi a napot, az az 20-25 percig gyakorol egy kicsit programozni. Mindenkinek ajánlom gondolja át ennek a módszernek a követését.

Elég sok tesztet írok munkám során, de mégis érdekes volt felismernem, hogy ezek egy része tesztelői feladat nem pedig a fejlesztés része. Ez persze nem feltétlenül baj, de egyáltalán nem ártott, hogy jobban tudatosítsam, hogy melyik része a fejlesztés és melyik már a tesztelés. Természetesen egy újabb bejegyzést lehetne írni arról, hogy a programozónak érdemes-e a tesztelés ezen oldalába belefolynia. Azért én résztvennék egy Testretreat-en is. :D

Az esemény végén mindenkinek feltettek három kérdést, amire mindenkinek kellett válaszolnia. Mit tanultál, mi volt a megdöbbentő és min fogsz változtatni hétfőtől a munkahelyeden. Azt hiszem onnan egy két gondolatot a szervezők kiírhatnának az esemény honlapjára, hogy megtudja ország világ a coderetreat áldásos hatásait.

Összefoglalva nagyon jól sikerült rendezvénynek tartom (ezúton is köszönet az emarsys-nek), akivel csak lehet megpróbálom megismertetni a gyakrolás semmivel sem összehasonlítható építő szerepét. Nem maradt más hátra: Gyakorolj, gyakorolj, gyakorol!

Sunday, March 11, 2012

API tesztelése, automatizált API teszt generálás

Az API-k általában jól meghatározható bemenettel és kimenettel rendelkeznek, alkalmat biztosítva automatizált tesztek generálására. Ezért is előny, ha egy webalkalmazásnál az adatokat, adatszerkezeteket egy API biztosítja, jól elkülönítve a felületet leíró szerkezetektől, rendszerektől. Általában ezek az adatok egy adatbázisból az API-n keresztül esetlegesen az API által manipulált formában kerülnek a klienshez.

Ilyenkor az első feladat, hogy biztosítsunk egy olyan teszt adatbázist, ami megegyezik az éles adatbázissal, de bármikor könnyedén fel tudunk építeni. Felmerülhet sok kérdés, például akkor, ha az adatbázishoz nem csak az API-n keresztül lehet hozzáférni, ami vagy hiba, vagy az adott rendszerhez át kell szabni a tesztelést. Egyelőre azt feltételezem, hogy kizárólag az API-n keresztül történnek az adatbázis műveletek. Hozzá kell tennem, hogy mi abban a szerencsés helyzetben vagyunk, hogy a szolgáltatásunkban, alkalmazásunkban nincs külső függőség, így könnyedén tudjuk automatizálni a tesztjeinket.

A fentieknek megfelelően egy teszt adatbázist kezelő scriptet készítettem.
$ ./testdb.py -h
Usage: testdb.py [options]

Options:
  -h, --help            show this help message and exit
  -s PATH, --struct_path=PATH
                        read descriptor struct.json file from PATH
  -o TYPE, --output-type=TYPE
                        generate output by TYPE
  -c COMMAND, --command=COMMAND
                        run test with COMMAND
  -q, --quiet           don't print status messages to stdout
  --host=HOST           database connect host

Működésének folyamata:
  • Létrehozzuk az adatbázist, ami ahhoz kell, hogy el tudjuk indítani az adott alkalmazást  vagy szolgáltatást.
  • Lefuttatjuk a scriptet "read" paranccsal, amely a megadott json fájlba leírja milyen táblákba, milyen adatokat kell beilleszteni.
  • Amennyiben tesztelni szeretnénk az adatbázissal, akkor nincs más hátra, mint felépíteni az adatbázist:
    • A teljes teszt adatbázis  törlésre kerül.
    • A sémakezelő létrehozza az adott branch-nek megfelelő adatbázist és a  táblákat.
    • Utána a script "insert" parancsával beillesztjük a szükséges rekordokat a már létrehozott json fájlból.
Ezt a teszt adatbázist minden etaponként/sprintenként karban kell tartani, azonban csak annyi a teendő, hogy a rekordokban történt változást/migrálást erre az adatbázisra is érvényesítenünk kell, a sémaváltozásokat meg úgy is a sémakezelő végzi.

Mivel van egy bármikor reprodukálható adatbázis, amit a tesztelés előtt és közben is akár újra és újra felépíthetünk, ezért neki is lehet kezdeni az API tesztelésének. Készítettem egy testapi.py scriptet, ami egy json fájlból olvassa ki a teszteket.
./testapi.py -h
Usage: testapi.py [options]

Options:
  -h, --help            show this help message and exit
  -t PATH, --tests-path=PATH
                        read test *.json files from PATH
  --test-file=FILE      read test *.json file
  -o TYPE, --output-type=TYPE
                        generate output by TYPE
  -c COMMAND, --command=COMMAND
                        run test with COMMAND
  -q, --quiet           don't print status messages to stdout
  -r, --resolve         interactive conflict handling
  --host=HOST           database connect host

A teszteket leíró struktúra http kérések tesztelésére lett kialakítva, nézzünk egy REST kérésre példát:
[
    {
        "url": "testdb.host.hu",
        "description": "Get user resource TEST",
        "name": "UserResources",
        "resetDb": true,
        "tests": [
            {
                "resource": "/users/6/",
                "method": "GET",
                "data": "",
                "expectedResult": {"userid": 6, "username": "test"},
                "expectedResponseCode": 200
            }
        ]
    }
]
A testapi.py kezeli ezt a struktúrát, amiben például lehetőség van session kezelésére nem nyilvános API-k esetében, vagy lehetőséget biztosít bizonyos kulcsok mellőzésére (ami dátumok és idők kezeléséhez is jól jöhet). Nézzünk akkor egy tesztet:
$ ./testapi.py --test-file=test.json -t test/ -c api -o console
Load test: test.json 
        apitest/test.function_one 
        apitest/test.function_two 
        apitest/test.function_three 
test_api_aux.TestAPI_Aux tests=6  errors=0 failures=0 Ok

----------------------------------------------------------------------
TESTS=6 ERRORS=0 FAILURES=0 
Ran 6 tests in 0.016s 

OK
Amint elrontunk valamit jelzi a hibát, jól látszik hogy a name kulcson nem a megfelelő nevet kaptuk vissza:
$ ./testapi.py --test-file=test.json -t test/ -c api -o console
Load test: test.json 
        apitest/test.function_one 
--- expected 
+++ server result 
@@ -1,5 +1,5 @@
 {
     "company": "Virtual Call Center Ltd.", 
-    "name": "Jack Test", 
+    "name": "Jack Test Bad Name", 
     "address": "Hackle Rise Street"
 }      
        apitest/test.function_two 
        apitest/test.function_three 
test_api_name.TestAPI_NAME tests=6  errors=0 failures=1 Error

----------------------------------------------------------------------
TESTS=6 ERRORS=0 FAILURES=1 
Ran 6 tests in 0.014s 

ERROR
Agilis fejlesztésnél igen gyakran változnak a dolgok, ezért nehéz karbantartani a teszteket. Amennyiben ez a változás a visszaadott eredményt érinti lehetőség van azt a -r opció segítségével interaktív módon érvényesíteni. Például ha bekerül az eredménybe egy új kulcs, akkor lehetőségünk van azt feloldani, így a test.json fájlba felülíródik a teszt, majd új futtatásnál már nem dob hibát:
$ ./testapi.py --test-file=test.json -t test/ -c api -o console -r
Load test: test.json 
        apitest/test.function_one 
--- expected 
+++ server result 
@@ -1,5 +1,6 @@
 {
     "company": "Virtual Call Center Ltd.", 
     "name": "Jack Test", 
+    "birth": "1980-03-12", 
     "address": "Hackle Rise Street"
 }
Resolve conflit (y/n): y
        apitest/test.function_two 
        apitest/test.function_three 
test_api_name.TestAPI_NAME tests=6  errors=0 failures=1 Error

----------------------------------------------------------------------
TESTS=6 ERRORS=0 FAILURES=1 
Ran 6 tests in 2.644s 

ERROR
$ ./testapi.py --test-file=test.json -t test/ -c api -o console -r
Load test: test.json 
        apitest/test.function_one 
        apitest/test.function_two 
        apitest/test.function_three 
test_api_name.TestAPI_NAME tests=6  errors=0 failures=0 Ok

----------------------------------------------------------------------
TESTS=6 ERRORS=0 FAILURES=0 
Ran 6 tests in 0.022s 

OK


API hívásokról van szó ezért könnyen automatizálhatóak ezek a teszt json fájlok létrehozása. Az API logokból könnyedén lehet generálni, ahogy mi is tesszük. Ez pedig azt jelenit, hogy a Mozmill segítségével elkészített  automatikus User Acceptance tesztek által generált API logokból mindig létre tudjuk hozni azoknak a forgatókönyveknek megfelelő API teszteket. Ami több dologra is jó:
  • Szűkíti a hiba forrásának a helyét.
  • Csökkenti a tesztelési időt.
  • Példaalapú tesztelésnél jó eszközt biztosít a szerveroldalhoz.
  • Elég egy helyen azaz a Mozmill-ben karbantartani a teszteket, hiszen sok más teszt abból generálódik le.
  • Integration tesztekhez is segítséget nyújt.
Természetesen ez a mi rendszerünkre lett kitalálva, de könnyedén átalakítható más igényekre is. A mi esetünkben ezek a scriptek a következő előnyökkel járnak az API tesztelhetőségén kívül:
Előnyök:
  • Példaalapú fejlesztést segíti, az API funkcionalitás elkészülte előtt írható rá teszt.
  • Alkalmas más, például biztonsági vagy stressz tesztek generálására.
  • Generálható felülettesztek által generált API logból.
  • Egyes új módosítások feloldására alkalmas, ami megkönnyíti a karbantartást.
  • Alkalmas junit formátumú xml kimenetet gyártani, ezért könnyedén beilleszthető például jenkinsbe.

Monday, March 5, 2012

Agilis tesztelés bevezetése a VCC-nél

Mikor 2006 májusában megérkeztem a Virtual Call Center Kft.-hez, akkor kizárólag manuális tesztelés folyt. Hallom, ahogy egy japán kisgyerek kezében kettétörik egy nógrádi ropogós a nagy ijedelemben, ahogy a szaharában 34 méteres cunami tombol és síri csend támadt az amazonasi őserdőben. Így a feszült figyelem középpontjában nem maradt más hátra, mint fejtegetni (vagy magyarázni) az okokat:
  • Nem volt annyi funkcionalitás, hogy ne lehetett volna könnyű szerrel kézzel tesztelni.
  • Nem volt annyi ügyfél, akik jogos felháborodásuk jeleként hibajegyeket generáltak volna a hibajegykezelőbe.
  • Nem volt elég tapasztalatunk a szoftvertesztelés terén.
  • A fenti háromnak is köszönhetően nem is gondoltunk másfajta tesztelésre.
Csakhogy az innovatív ötletek és a lelkes csapat segítségével kialakított a kategóriájában piacvezetővé váló alkalmazás hamar elvette a kényelmességhez hozzászokott kedvünket. Sok ügyfél, megszámlálhatatlan funkcionalitás.  A teszteléssel való lemaradásunkat fokozta, hogy bevezettük az agilis fejlesztést, aminek a részeként az ügyfél igények figyelembevételével zajlott a fejlesztés, ami a funkcionalitások számának még gyorsabb tempóban való növekedéséhez vezetett. Ennek a lemaradásnak az érezhető jelei igen hamar jelentkeztek a fejlesztők egy részénél, ezért kalóz tesztelés vette kezdetét. A fejlesztés felpörgetése miatt aktuálissá vált refaktorok nehezedése miatt is páran elkezdtek unit teszteket írni. Kutatások folytak a felülettesztelés irányában is, de nem igazán volt összeszedett, megmaradt a kalózkodás szintjén. A vezetőség is látta a nem lesz ennek jó végét, így szabad utat kapott a nagyüzemi kutatás, a mégis hogyan kéne azt csinálni címmel.

Elkezdődött a rengeteg könyv olvasása, meetup-okon talán már egy kicsit idegesítően sok kérdés feltevése, valamint az otthoni nyugodt szárnypróbálgatások izgalmas, fejlődést elősegítő programjain vettünk részt és nem utolsó sorban rengeteg problémamegoldó vita arról, hogy mikor mit érdemes és hogyan tesztelni ( Farkas Máté kollégám biztos sokat tudna mesélni a vitáinkról :) ). Az ezekből szerzett ihlet és inspiráció hatására:
  • Minden területen elkezdődtek a unit tesztek írása, a fejlesztés segítésére és a hibajavítás megkönnyítésére.
  • Kialakult a felülettesztelésre alkalmas környezet,  ami számunkra a mozmill-t jelenti.
  • Kialakult az automatizált és manuális biztonsági tesztelés.
  • Szimulátorok készültek komplex hívásfolyamatok stressz- és rendszertesztelésére.
  • Automatikus integration tesztek kerültek megírásra.
  • Felületen történt események rögzítésére majd abból tesztek generálásra alkalmas eszköz készült API tesztelésének céljából.
  • Bevezetésre került az exploratory tesztelés.
Ezekről a tapasztalatokról fogok írni ebbe a blogba folytatásként, először az automatikus API teszt generáló eszközről.

Saturday, June 18, 2011

Agilis tesztelő kihívásai

Jó agilis tesztelőnek lenni egészen összetett dolog. Az agilis szoftverfejlesztésnél idális esetben alapvetően két csapatot határozhatunk meg, a “customer team”-et valamint a “developer team”-et, melyek mindegyikébe beletartozik a tesztelő. Ügyfél oldalról segíti a projekttel kapcsolatos igényeket tesztesetekre bontani, a fejlesztői oldalról pedig a tesztek sikeres futását, újrafelhasználhatóságát, kis lépésekben történő folyamatos fejlődését kell biztosítania. Ehhez rengeteg kommunikációra és megfelelő technikai képzettségre van szükség.

Mindez még csak a kezdet, ha az adott tesztelő specialista még nem találkozott az agilis szoftverfejlesztés világával. Nem elég, hogy merőben újfajta szemléletek és helyzetek végelláthatatlan seregével kell megismerkednie, azt jól is kell alkalmaznia, kezelnie. Egyik nagy kihívás nem csak a tesztelőnek, de minden egyes új az agilis szemlélettel ismerkedőnek, hogy felvegye a csapattal a tempót. Ez nem azért van így mert az agilis csapattagok az életüket félredobva feláldozzák magukat a minőségi szoftverfejlesztés oltárán, hanem mert ők már sikeresen alkalmazzák az agilis elveket. Nem dolgoznak többet csak sokkal hatékonyabban teszik mindazt az olyan projektekben, amelyeknél alkalmazható az agilis módszertan.

Az a tesztelő aki ahhoz szokott, hogy egy tesztelő csapat tagjaként részletes útmutatás segítségével (például hónapok alatt elkészült specifikáció alapján) írja a teszteket, az meglepődhet, hogyan lehet elérni, hogy minden teszt sikeresen elkészüljön két-három hetes ciklusban, folyamatosan változó funkcionalitás mellett. A tesztelés egyik céljaként hozzá kell segíteni a csapatot, hogy minden egyes kiadásnál értéket adjon a szoftverhez. Egyik kulcsa ennek, hogy a jelentkező hibákról azonnali visszajelzést adjon annak a csapattagnak, aki javítani tudja azt. Így előfordulhat az, hogy a tervezettnél kevesebb új funkció kerül bele a soron következő kiadásba, mert a hibajavítás időt vesz el, de cserébe magasabb minőségű funkciók kerülnek kiadásra. Ezáltal elkerülve a következő iteráció elején a sok hibajavítást, ami csak feszültséghez vezet.

Ezeknél a visszajelzéseknél fel kell készülnie a tesztelőnek, hogy gyakran heves ellenállásba fog ütközni, főleg olyan fejlesztőknél, akik még nem tudták, vagy nem is nagyon akarják elsajátítani az agilis fejlesztéssel járó szemléleteket, valamint nem értik a tesztelés célját. Ilyenkor a fejlesztők nem igazán örülnek, hogy találtak hibát a munkájában, hiszen jobb azt hinni, hogy tökéletesek vagyunk, arról nem is beszélve, hogy sok fejlesztő úgy gondolja, hogy a tesztelő inkább alsóbbrendű szolga mint munkatárs (egy-két pofont ilyenkor ki kell osztani). :) Ebből a szempontból az egyik oldalról tudatosság és bátorság kell, a másik oldalról pedig további fejlődés szükséges ahhoz, hogy a csapat munkáját elősegítsék ezáltal a szoftver minőségét biztosítsák. Amennyiben a két fél között emiatt tartós feszültség lép fel, akkor ahhoz is bátorság kell, hogy a tesztelő jelezze a menedzser felé a munka akadályozásának tényét, ami azért is nehéz kérdés, mert a nem a tesztelő hatásköre, hogy a csapat minden tagja megfelelően végezze a munkáját.

A tesztelőnek ( többek között a fejlesztőknek is amikor pl. a TDD-t alkalmazzák ) fel kell készülniük arra, hogy többször is el fognak bukni. Ilyenkor erő és kitartás kell ahhoz, hogy tanuljunk a hibáinkból, ne pedig feladjuk és azt mondjuk, hogy ez nem is működik, csak mert nem sikerült elsőre. Például megtanulni görkorcsolyázni hasonló dolog. Nem lehet úgy elkezdeni, hogy elsőre ne szántsd fel a betont, aki ennek ellenére is folytatja, az megtanul görizni, aki nem az pedig nem állíthatja, hogy nem jó dolog, csak azt hogy neki sikerült elég hamar feladnia. Aki fél az eséstől, annak hamar elmegy a kedve a teszteléstől. :)

Fel kell készülni a folyamatos automata tesztek kialakítására és fejlesztésére is. A cél: a lehető legkisebb költséggel karbantartható környezetet kialakítása, amely alkalmazkodik a gyakran változó igényekhez, és amely biztosítja a gyors visszajelzés lehetőségét. Ez sok kutatást, utánajárást, fejtörést vagy akár fejlesztést igényel. Például ha van egy egyelőre nehezen tesztelhető része a szoftvernek, amire nem igazán illeszkedik egyetlen meglévő megoldás sem, akkor előfordulhat, hogy írni kell egy saját szimulátort, amivel tesztelhető a működés a teljesítmény stb. Ilyenkor együtt kell tudni működni a fejlesztővel. A tesztelőnek folyamatosan fejlesztenie kell a tesztelő rendszert, hogy a tesztelés minden területén a lehető legjobb és legegyszerűbb eszközt használják. Ami beválik azt alkalmazni kell, ami nem azt nem kell erőltetni, el kell dobni hamar.

A két hetes ciklusokban folyamatosan változik a funkcionalitás, ezáltal törekednie kell a tesztelőnek, hogy tudjon alkalmazkodni ezekhez a változásokhoz, ezért újrafelhasználható teszteket kell alkalmazni ott ahol csak lehet. Képesnek kell lennie arra, hogy az adott ciklusban történt változásokat összeszedje, ha kell, akkor megbeszélje a fejlesztővel, hogy mi is változott, majd a meglévő teszteket átírja a változásoknak megfelelően. Itt is fontos, hogy a tesztelő ne hagyja magát elhessegetni, mert pl. a fejlesztő úgy gondolja éppen nem ér rá.

A fejlesztők azt szeretnék, ha a tesztelő jártas lenne a technikai dolgokban, amik alkalmazva vannak az adott projektben minden apró részletére kiterjedően. Sokan közülük nem tudnak vagy nem is akarnak tanítani. Így idegbetegek lesznek, ha olyan dolgot kell elmagyarázni a tesztelőnek, amely ahhoz szükséges, hogy megértse azt a másik témát amiről éppen beszélni akarnak. Annak ellenére, hogy többnyire (tisztelet a kivételnek) elég szűk látókörrel rendelkeznek más téren, de hát ha egyszer hazai pályán vannak, akkor büszkék a tudásukra (na már megint kell egy kicsit pofozkodni). :) Ez a technikai tudásbeli különbség csökkenthető folyamatos önfejlesztéssel, valamint egy kis hozzáállás javítással a fejlesztői oldalról.

Ezután jön a meglepetés, amikor az ügyfél oldali csapatban van teendő. Az ügyfél vagy annak képviselője szeretné, ha értenék azt amit mond, és a fejlesztőkkel sok órányi megbeszélés is szükséges lehet ahhoz, hogy megértsék egymást és akkor is mást fognak lefejleszteni, mint amit az ügyfél akart. Ilyenkor a tesztelőnek kell lennie a két oldal közötti kapocsnak, és közös nyelvként a teszteseteket kell használni. Itt fontos szerepe van a tesztelőnek, hisz olyan példákat kell találnia, ami az egyik oldalról lefedi az igényeket a másik oldalról meg technikailag kivitelezhető esetet jelent.

Vannak olyan ügyfelek, akik nem tudják pontosan megfogalmazni mit is akarnak igazán. A fejlesztés közbeni folyamatos visszajelzéssel lehet ezen segíteni, de a másik megoldás lehet, hogy a tesztelő segít  megfogalmazni az igényt. Ilyenkor látni kell a "big picture"-t, teljesen ellentétben a fejlesztői oldalon lévő kommunikációval, ahol kis részekre vonatkozóan is kell tudni gondolkodni. Sokszor megérzésekre vagy fantáziára van szükség ezekben az esetekben. Ezáltal a tesztelőnek kétféle látásmóddal is kell rendelkeznie, ami nem igazán egyszerű.

A fentiekből látszik, hogy nem feltétlenül a tesztelőnek van a legkönnyebb dolga a csapatban. Bár a nehézségek közül egyszerre ritkán van jelen az összes, de tudni kell kezelni mindegyiket. A lényeg, hogy a tesztelő is a csapat teljes értékű fontos része, aki segíthet, hogy egy igazán jó szoftver készüljön. Az agilis tesztelő olyan munkát végezhet, ami minden területen változatosságot és fejlődést igényel.

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.