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.