Komplex példa - Validálás - 6. rész: Szabályok tesztelése (Laravel Dusk)

Attila | 2022. 08. 12. 11:29 | Olvasási idő: 7 perc

Címkék: #Adatgyár (Factory) #Dusk #Érvényesítés (Validation) #FeatureTest #Laravel #PHPUnit #Tesztelés (Testing) #UnitTest #Űrlap (Form)

Teszteljük az űrlapjainkat validációs szempontból! Először megtervezzük a tesztjeinket, teszteseteinket: érvényes és érvénytelen kimeneteket egyaránt fogunk vizsgálni. Aztán végre is hajtjuk ezeket a Laravel Dusk segítségével. Közben pedig tovább mélyítjük a tudásunkat a szoftvertesztelés kapcsán.
testing_form_validation

Bevezetés

Előzmények

Mivel már régebben foglalkoztam teszteléssel, ezért érdemes lehet feleleveníteni a teszteléssel kapcsolatos bejegyzéseimet (aki még nem tette volna meg, érdemes lehet ezen bejegyzés olvasása előtt végigfutni az alábbi bejegyzéseket, főleg a 2-3. pontban említetteket):

  1. A teszteléssel legelőször a példaadatok generálásánál találkoztunk. Ehhez a Laravel biztosítja nekünk a gyár (factory) és a seeder osztályokat.
    • A gyárakkal először itt találkoztunk. Aztán még jónéhányszor kapcsolatba kerültünk velük, mivel sokszor szükségünk volt mintaadatokra, hogy látszódjanak a megvalósított funkcionalitásaink eredményei.
    • Amikor több gyárat együttesen akartunk működtetni, akkor volt nagyon hasznos számunkra a seeder osztályok alkalmazása.
  2. A Laravel-ben használható tesztelés alapjaival és a PHPUnit lehetőségeinek használatával itt ismerkedtünk meg.
  3. Aztán pedig egy másik specifikus eszközzel, a Laravel Dusk-kal végeztünk gyakorlati feladatmegoldásokat, amit kifejezetten a tesztelés megkönnyítésére, automatizálására, látványosabbá tételére hoztak létre.

Cél, motiváció

A mostani bejegyzésben a validációs fejezetet szeretném lezárni, és olyan automatikus teszteket létrehozni, amelyek utána mindig biztosítják, hogy a már megvalósított érvényesítési (validációs) eljárásaink továbbra is jól működnek az űrlapjainknál. Így az alkalmazásunk minősége megmaradhat olyannak, amilyet mi elterveztünk és megvalósítottunk neki.


Tesztesetek megtervezése és végrehajtása

Szoftvertechnológiai szempontból a tesztelésnek mindig kiemelt szerepe van a szoftver életciklusa során: a különböző fázisokat külön-külön és egészében is tesztelhetjük, illetve elvárás is, hogy teszteljük azokat. Mi most az űrlapjaink kitöltésének ellenőrzésére fogunk koncentrálni.

Informatikusként, programozóként, adatbázis tervezőként, adatelemzőként, ... picit nehéznek tűnhet teszteseteket megtervezni és írni, mivel ez is egy művészet, külön rá kell érezni és egy külön szakma épül erre, különböző vizsgázási lehetőségekkel is. Én most a tesztelés teljes mélységébe nem mennék bele, de itt van egy kiváló dokumentum, amit szívesen ajánlok, mert elmagyarázza a tesztelés alapjait, technikáit, menedzselését stb. Ha pedig ennél is mélyebben érdeklődik valaki a téma iránt, akkor feltétlenül olvassa el Robert C. Martin - Tiszta kód című alapvetését, amit amúgy minden szoftverfejlesztőnek ajánlok.

De nem szeretnék túlságosan elkanyarodni a témánktól és próbálom tartani a fókuszt a validáció tesztelésén. Nézzük meg inkább a folyamatot, amit szeretnék tesztelni:

  1. Bemenetek generálása
  2. Kliens oldali validációs szabályok ellenőrzése (pozitív és negatív szempontból is) űrlap elküldésekor
  3. Szerver oldali validációs szabályok ellenőrzése (pozitív és negatív szempontból is) űrlap elküldése után

Bemenetek generálása

Érdemes felmérni az űrlapon leggyakrabban előforduló bemenettípusokat, érvényes és érvénytelen bemeneteket azért, hogy tisztában legyünk vele, milyen módokon is lehet majd tesztelni őket. Azért is fontos ezeket már ilyenkor megcsinálni, mert ha véletlenül valamit a szoftverfejlesztő rosszul implementált vagy elnézett valamilyen kombinációt, amely bekövetkezése miatt a felhasználói elfogadási teszt (User Acceptance Testing) vagy a rendszerteszt (System Testing) során hibát kapunk, akkor az nagyon "ciki" tud lenni, úgyhogy jobb elkerülni az ilyen szituációkat.

Bemenet típusok
Érvényes bemenetek
Érvénytelen bemenetek
Numerikus érték (egész vagy lebegőpontos szám)
  • csak szám típusú értékek
  • kisebb, mint a megadott maximum limit ÉS nagyobb, mint a megadott minimum limit
  • számok ÉS (betűk VAGY különleges karakterek)
  • nagyobb szám, mint a megadott maximum limit
  • kisebb szám, mint a megadott minimum limit
  • esetleg a negatív számok
  • esetleg a 0
  • lebegőpontos számoknál a "tizedesvessző" (pont?)
Szöveg (karakter vagy karakterlánc)
  • csak betűk
  • csak számok
  • csak speciális karakterek
  • az iménti három tetszőleges kombinációja
  • kisebb hosszúságú (rövidebb), mint a maximális karakter limit ÉS nagyobb hosszúságú (hosszabb), mint a minimális karakter limit
  • minimális VAGY maximális karakterlimiteken túllépő
Dátum és/vagy idő
  • dátum/időválasztó segéd látható, ha megkapja a fókuszt a mező
  • megfelelően működik az év / hónap / nap / óra / perc léptetése
  • az iméntiekkel egyidejűleg a manuális szerkesztését a mezőnek letiltani (csak a segéddel - picker - lehessen módosítani az értékeken)
  • megfelelő formátumú dátum/idő (webes alkalmazásoknál böngésző nyelvi vagy lokalizációs beállítás függő)
  • szökőévben február 29. ellenőrzése
  • nem szökőévben február csak 28 napos, ennek ellenőrzése
  • óraátállításos napok időpontjainak ellenőrzése
  • téli/nyári időszámítás ellenőrzése
  • időzónák ellenőrzése
  • dátum/idő kapcsán nagyon sok mindent lehet tesztelni, de ha az érvénytelen bemenetekre akarunk koncentrálni, akkor az itt felsorolt érvényesek ellentettjét ellenőrizzük

Bemenet típus lehet még a listából (select options, radio buttons, checkbox lists) választás, de itt általában elég megkövetelni azt, hogy kötelező-e kiválasztani valamit vagy sem.

Dinamikus tesztek tervezéséhez a következőket hajtsuk végre a tesztesetek kapcsán:

  1. Egy teszteset végrehajtása esetén a rendszert egy megadott kezdő állapotba kell hozni (előfeltételek --> lásd a következő alfejezet "Tipp" részét).
  2. Megadott bemeneti (input) értékek halmazával végre kell hajtatni a tesztelt elemet, szoftver egységet (ezeket a fenti táblázatban részleteztem).
  3. Majd a teszt futásának eredményét össze kell hasonlítani az elvárt eredménnyel (ezt nyilván a jól megírt tesztesetünk fogja ellenőrizni: hiba esetén jelzi, hogy mi volt a gond, ellenkező esetben megfelelően végrehajtódik a teszt).
  4. Végül ellenőrizni kell, hogy a végrehajtás után a rendszer az elvárt állapotba (utófeltételek) került-e: itt például gondolhatunk arra, hogy a tesztadatbázis érintett tábláiból kitörölhetjük az adatokat, amiket a tesztesetek lefutása generált.


Rázódjunk vissza! - Eddigi tesztjeink áttekintése

Mielőtt még a validációs szabályok ellenőrzésébe belevágnánk, tekintsük át, hogy jelenleg milyen tesztjeink, teszteseteink vannak, futtassuk az utasítást:

php artisan test

Amivel a következő eredményt kapjuk:

Mindegyik tesztünk pozitívan lefutott, zöldek vagyunk, örülünk. Ezek a tesztek a tests / Unit és a tests / Feature almappákban vannak és ha megnyitjuk, végiggörgetjük őket, akkor láthatjuk, hogy ezek főleg útvonal példák vagy adatbázis gyárral (factory) kapcsolatos tesztesetek voltak.

De, ha egy pillanatra megállunk és felidézzük Laravel Dusk-os emlékeinket, akkor megnézhetjük a tests / Browser mappát is, amiben ott van szintén egy példa, de van nekünk egy utasokat létrehozó tesztesetünk is, a PassengerTest osztály.

Tipp: mindenekelőtt, ha még nem tettük volna meg, indítsuk el az alkalmazásunk kiszolgálását (php artisan serve), majd mielőtt belevágnánk a további tesztelgetésbe, és főleg, ha már régebben használtuk a Laravel Dusk-ot a Chrome böngészőben, akkor érdemes frissíteni a driver-ét: php artisan dusk:chrome-driver

Futtassuk is a php artisan dusk tests/Browser/PassengerTest.php parancsot, aminek hatására megnyílik a Chrome böngészőnk, az utasokat létrehozó űrlappal. Az utas neve kitöltődik és a repülőjárata kiválasztódik, viszont a többi (azóta hozzáadott bemeneti elem piros keretű marad) és az adatokat nem lehet elküldeni a Mentés gomb "megnyomásával". (Emlékezzünk! Az iménti utasítás hatására a tesztelésre használt üres adatbázisunkat - laravel_v9_dusk - fogja használni a rendszer és feltölteni a flights adattáblánkat azzal a 3 repülőjárattal, ami ennek a tesztesetnek az elején kerül definiálásra és létrehozásra.) A lefutás egy részlete itt látható:


A böngésző jó gyorsan be is záródik és megkapjuk a visszajelzést a hibáról a terminal-ban:

Sárgával kiemeltem azokat a részeket, amik számunkra most fontosak. Tehát van 1 hibánk, méghozzá a a_visitor_can_create_a_passenger függvényen belül, méghozzá az, hogy a Mentés gomb nem kattintható, írja is, hogy a 24. sorban van a kattintás meghívása nálam.

És miért nem volt kattintható? Pontosan azért, mert ennyi adat kitöltése nem felelt meg a teljes űrlap validációjának. Javítás: ez inkább a --headless Dusk beállítás miatt nem volt kattintható, a magyarázatot lásd később.

Én mindig azt mondom (kevésbé "TDD-látásmóddal"), hogy először hozzunk össze egy működő funkcionalitást, utána végezzük el a finomhangolásokat, mint például a validáció. Lehet ezt pont fordítva is csinálni, hogy minden egyes űrlap mezőt teljesen részletesen végiggondolunk, hogy milyen probléma lehet vele és emiatt milyen validációt is alkalmazzunk nála, majd csak utána lépünk tovább a következő űrlapelemre, ha már ez az adott megvan minden szempontból.

Az iméntiek miatt tesztelési szempontból én azt javaslom, hogy először készítsünk el (illetve bővítsük ki a már meglévő) tesztesetünket olyanra, ami érvényes (valid) eredményt produkál a kliens és szerveroldali validáció szempontjából. Emlékeztetőül, ezzel az üres űrlappal foglalkozunk:

(Látható is, hogy a Mentés gombunk nem kattintható, amíg érvénytelenek a bemenetek.)

A tesztesetünk az utas nevét és a repülőjáratát kiválasztja, viszont az életkort, e-mail címet, telefonszámot nem, továbbá a születési dátum alapértelmezett értéke a mai napra van állítva, ami biztosan nem lesz jó, hiszen ennek legalább 18 évvel korábbi dátumnak kell lennie (bármit is állít a felhasználó az életkor mező kitöltésénél). Mi, mint akik programoztuk az alkalmazást, ismerjük a feladatunk specifikációját és ismerjük a kliens/szerver oldali validációnk működését is, emiatt fogunk tudni olyan teszteset megadni, aminek érvényes lesz a kimeneti eredménye, és olyat is, aminek érvénytelen.

Egy gondolat még kiegészítésül az úgynevezett ekvivalencia osztályokról: nekünk nem kell, például az életkor kapcsán, az összes értéket végigtesztelni 6-tól 99-ig, mert az ezek közötti számok mindannyian megfelelőek, ezért ebből a halmazból mindössze egy értéket érdemes kiválasztani, amivel az egész halmazt tudjuk reprezentálni. Továbbá pont emiatt érdemes az érvénytelen (invalid) tesztelésre egy-egy számot választani a táblázat felsorolásából (halmazaiból, ekvivalencia osztályaiból), ami mondjuk 6 alatti vagy ami 99 feletti, a jelenlegi példánkban. De sokszor a negatív számok vagy a 0 okozzák a problémát, érvénytelenséget, úgyhogy ezekre különösen figyeljünk és teszteljük őket. Ami még kimondottan problémás szokott lenni, úgyhogy ezeket szokták is tesztelni, azok a határokon lévő értékek, az életkornál például a 6 még jó érték vagy már nem?


Utas létrehozási teszt kibővítése

1. példa: az érvényes (valid) kliens és szerver oldali teszteset

Először tehát egy érvényes tesztesetet hozzunk létre. Ez például egy megfelelő:

$this->browse(function (Browser $browser) {
  $browser->visit('/passengers/create')
    ->type('utasneve', 'Gludovátz Attila') // input mező name attribútum értéke
    ->type('age', 32)
    ->keys('#birthdate', '1990', '{tab}', '01', '01')
    ->type('email', 'attila@gludovatz.hu')
    ->type('phone', '20/123-4567')
    ->select('repulojarata')
    // ->click('input[type="submit"]')
    ->click('@save-button')
    ->assertSee('Gludovátz Attila');
});

Ahogy említettem, az "utasneve" és a "repulojarata" mezők kitöltése már megvolt korábban. Az új értékeknél az "age", "email", "phone" nem okozhatott problémát, könnyű volt neki érvényes értékeket megadni. A trükkös megoldás az a date típusú input mezőnél adódott, itt több dologra is kellett figyelni:

  1. A "keys" kulcsszót kellett használni, mintha a billentyűzetet (keyboard) használnánk.
  2. A metódus első paraméterét selector-ként kell megadni, tehát mint a CSS-ben vagy a Javascript-ben, itt is a # szolgál az id HTML attribútum megjelölésére.
  3. A további paraméterek pedig arra vonatkoznak, amit beírnánk mi magunk is a billentyűzettel, tehát először az évszám, aztán egy TAB billentyű majd egymás után közvetlenül a hónap és a nap száma, a fenti példában ez 1990. január 1. lesz.

Az alkalmazást teszteljük ezzel az utasítással, hogy színes (zöld vagy piros) legyen az eredmény:

php artisan dusk tests/Browser/PassengerTest.php --colors=always

Viszont még hibánk van... Ez így még mindig nem elég ahhoz, hogy megfelelően működjön a teszteset, mivel két további Dusk beállítás is akadályozhat minket a végső sikeres (érvényes) lefutás elérésében. Nyissuk meg a DuskTestCase.php beállításokat tartalmazó fájlt és a driver metódus $options beállításaira koncentráljunk. Kiemeltem alább azt a két sort, amivel foglalkoznunk kell:

$options = (new ChromeOptions)->addArguments(collect([
  '--window-size=1920,1080',
  '--lang=hu_HU',
])->unless($this->hasHeadlessDisabled(), function ($items) {
  return $items->merge([
    '--disable-gpu',
    '--headless',
  ]);
})->all());
  1. Az első beállítás az a nyelvre vonatkozik. Nem meglepő, mert már az előző bejegyzésben is említettem, hogy a dátumok formátuma nagyban függ a nyelvi / lokalizációs beállításoktól. Itt beállítjuk, hogy a dátumformátum magyar legyen. Enélkül ugyanis a korábbi keys() metódusban kiadott paraméterek nem lennének megfelelőek.
  2. A másik még trükkösebb beállítás a --headless kapcsoló beállítása, tehát a kommentezés kivétele. Ez nálam problémás volt, mivel notebookon fejlesztek és az utas létrehozó űrlap nem fért ki teljes egészében a képernyőmre, amikor a Dusk végezte a tesztadatok kitöltését a Chrome-ban. Emiatt mindig olyan hibára futott a teszt, hogy az űrlap végén lévő gombot nem lehet megnyomni, vagy "nem megnyomható". Ez csakis akkor működött, amikor a tesztkitöltés közben legörgettem és a tesztelő alkalmazás akkor már látta a gombot, amit meg is tudott nyomni, ekkor tehát a teszteset végrehajtása már zöldre váltott.
    • Ez a hiba vagy probléma persze nem merült volna fel talán, ha elég nagy kijelzőn teszteltem volna az alkalmazást.
    • Így a --headless kapcsolóval viszont nem látjuk élőben a kitöltést, "csak" egy végeredményt kapunk arról, hogy sikeresen lefutott a tesztünk.

Ez volt tehát az érvényes tesztünk lefuttatása, hiszen a click('@save-button') esemény után kijutunk az utasok index oldalára és már látni is fogjuk az újonnan létrehozott utasunk nevét.

2. példa: az érvénytelen (invalid) kliens oldali teszteset

Készítsünk egy olyan tesztesetet, ami elbukik a validáció "eltörése" miatt. Ehhez persze jó, ha tisztában vagyunk azzal, hogy maga a Laravel milyen lehetőségeket biztosít az assert utasításokra, ezeket itt tudjuk böngészni.

Ha mondjuk az előző PassengerTest-ben lévő tesztesetet vesszük alapul és bármelyik bemenet kitöltését kikommentezzük, például a telefonszámét, akkor már el is fog bukni a tesztesetünk.

Itt most már a --headless kapcsoló nem tud "bezavarni az erőbe", ennek a tesztnek így tényleg el kellett bukni. De mit kellene tenni ahhoz, hogy bár a validáció nem teljesült, mi kapjunk egy kliens oldali visszajelzést...? Ez abból a szempontból nehezebb, hogy a jelenlegi alkalmazásunkban addig nem is enged kattintani a rendszer a Mentés gombra, amíg nem érvényes minden egyes űrlapelem (a telefonszám hiánya pedig érvénytelenséget eredményez, természetesen).

Úgyhogy szüntessük meg ezt a beállítást. A resources / sass / mystyle.scss fájlban kommenteljük ki a form:invalid input[type="submit"] selector-ra vonatkozó részt, majd futtassuk a terminal-ban az npm run dev parancsot.

Állítsuk vissza a validáció szempontjából sikeres tesztesetünk és a tartalmát másoljuk le egy új metódusba, majd ott "rontsuk el" ismét, mint az alábbi kitöltésnél.

Talán jogosan merülhetne fel a dolog, hogy ezt a kis tooltip-et (figyelmeztetést) látnunk kellene az oldalon, úgyhogy ezekután itt van az új metódusunk:

/** @test */
public function a_visitor_cant_create_a_passenger_without_phone_number()
{
  Flight::factory()->count(1)->create();

  $this->browse(function (Browser $browser) {
    $browser->visit('/passengers/create')
      ->type('utasneve', 'Érvénytelen János')
      ->type('age', 22)
      ->keys('#birthdate', '1999', '{tab}', '12', '31')
      ->type('email', 'janos@invalid.hu')
      // ->type('phone', '20/123-4567')
      ->select('repulojarata')
      ->click('@save-button')
      ->assertSee('Kérjük, töltse ki ezt a mezőt.');
  });
}

Az iménti "látnunk kellene az oldalon" kifejezést az assertSee() metódussal tudjuk ellenőrizni. Ha viszont újra futtatjuk a tesztünket, akkor a következő visszajelzést kapjuk:

A Dusk keresi a megadott szöveget az oldal forrásában, azonban tényleg nincs benne, úgyhogy nem is látja így, jogos tehát a hibajelzés.

Ezekután már talán gondolhatjuk, hogy ha az oldal forrásában nincsen benne, akkor ez egy Javascript kód lesz. Az "assertSee"-s sort tehát kivehetjük a kódunkból (a végén a pontosvesszőt azért hagyjuk meg), majd folytathatjuk a Javascript kód ellenőrzésével a megvalósítást:

$message = $browser->script("return document.getElementById('phone').validationMessage")[0];
$this->assertEquals('Kérjük, töltse ki ezt a mezőt.', $message);

(Megjegyzés: a Laravel Dusk-nak nincs assertEquals metódusa, ezt a PHPUnit segítségével tudjuk használni.)

Fontos, hogy ezeket az utasításokat az előbbi kód után illesszük be, mivel az ott elért állapotban lesz már meg a phone mezőnek a validációs üzenete, amit aztán a következő sorban össze tudunk hasonlítani az általunk megadott szöveggel. Ha így futtatjuk a tesztet, akkor most már OK végeredményt kell visszakapnunk az assertEquals(...) utasításra.

Ha egy picit "túl is szeretnénk biztosítani" magunkat, akkor még a következő utasítást is beilleszthetjük az iméntiek után:

$browser->visit('/passengers')
  ->assertDontSee('Érvénytelen János');

Ezzel ellenőrizhetjük, hogy ha meglátogatjuk az utasok index oldalát, akkor tényleg nem történt meg a hozzáadás és nem látja (assertDontSee) a Dusk az új, érvénytelen utast. Erre persze már mondhatnánk, hogy szükségtelen megtekinteni, de itt a tanulási folyamatunk során talán nem haszontalan ezt így végiggondolni.

Ezen a ponton érdemes talán a kód teszt lefedettségéről beszélni. Ez a számszerű értékelése annak, hogy a tesztelési tevékenység mennyire alapos, milyen a minősége. A lefedettség egy mérőszám, amivel gyakorlatilag megmondhatjuk, hogy az implementált kódunkat milyen mértékben teszteltük. Például, ha csak ezt az utas létrehozási űrlapot tekintjük, akkor teszteltük az érvényes tesztesetet és az érvénytelenek közül egyet a kliens oldaliak közül, amikor hiányzott a telefonszám kitöltése. Nem teszteltük viszont még a kliens oldali tesztesetek közül azt, amikor például a név, életkor, születési dátum hiányzik, vagy például nem elég hosszú a név (legalább 10 karakteres), vagy a születési dátum nincs összhangban a megadott életkorral és így tovább... Jó sok mindent nem teszteltünk még, ezt talán érezzük, mint ahogy azt is érezhetjük, hogy minden egyes eset tesztelése elég időigényes (és valljuk meg, unalmas is lenne). Ezért a rendszerek, webalkalmazások nem is feltétlenül szokták elérni a 100%-os kód lefedettséget, hiszen ez sokkal több energiabefektetéssel (pénz+idő) járna, mint amit talán megérne. Vannak azonban biztonságkritikus rendszerek, mint például egy webes bankolási oldal, ahol a 100%-os kódlefedettség elvárt (az ügyfelek részéről is), hiszen itt még véletlenül sem történhet meg, hogy valamiről elfeledkeztek a fejlesztők. Persze ott is előfordulhat, hogy valaki hibázik, hiszen annyi mindent kellene tesztelés alá vonni... én is csak egyetlen űrlapról beszéltem eddig, miközben volna még jónéhány, amit el kellene látni teszteléssel, továbbá egyéb teszteseteket is vizsgálni kellene, például integrációs, rendszer és elfogadási teszteket is végre kellene hajtani, ügyelve mindig a biztonsági paraméterekre.

3. példa: érvénytelen (invalid) szerver oldali teszteset

Maradjunk viszont az űrlap ellenőrzésénél, mivel nem beszéltünk még a szerver oldali validáció teszteléséről. Annyiból szerencsénk van, hogy ha az 1. példában látható érvényes teszteset átment a kliens oldali ellenőrzésen és látható az új utas az index nézet oldalán, akkor gyakorlatilag a szerver oldali validáció is sikeresen futott le. Ami problémásabb lehet, az megint az érvénytelen eshetőség, vagyis az, amikor kliens oldalon elfelejtett valamit ellenőrizni a programozó (vagy éppen megtámadták a kódját kliens oldalon és olyan adatokat küldtek el, ami átment a kliens oldali ellenőrzésen), ilyenkor van leginkább szükség a szerver oldali validációra, amit mi most tesztesettel fogunk ellenőrizni.

Előfordulhat, hogy utasokat mostantól kezdve már nem 10 karakterű névvel, hanem legalább 5 karakterű névvel lehet csak felvenni a rendszerbe (hiszen nem szeretnénk mondjuk kizárni utasaink közül Kiss Imrét, akinek rövidebb a neve, mint 10 karakter, konkrétan 9 karakteres a szóközzel együtt). Mi pedig, mint gyarló emberek, ezt csak a kliens oldali szabály frissítésénél tesszük meg, a szerver oldali ellenőrzést változatlanul hagyjuk. A nézeteknél passengers / create.blade.php-ben az "utasneve" input mező "minlength" attribútum értékét írjuk át 10-ről 5-re (és érdemes a fájl alján a Javascript kódban is a figyelmeztető üzenetben a 10-et 5-re cserélni - ha ez problémát okozna, akkor a bejegyzés végén lévő Github commit fájlhoz tartozó tartalmát érdemes megnézni).

/** @test */
public function a_visitor_cant_create_a_passenger_with_too_short_name()
{
  Flight::factory()->count(1)->create();

  $this->browse(function (Browser $browser) {
    $browser->visit('/passengers/create')
      ->type('utasneve', 'Kiss Imre')
      ->type('age', 22)
      ->keys('#birthdate', '1999', '{tab}', '12', '31')
      ->type('email', 'imre@kiss.hu')
      ->type('phone', '20/123-4567')
      ->select('repulojarata')
      ->click('@save-button')
      ->assertSee('Kiss Imre');
  });
}

Látható, hogy szépen megadunk minden helyes(nek gondolt) értéket a tesztben, viszont nem lesz sikeres a teszt lefutása, mert nem látjuk a végén "Kiss Imre"-t az utaslistában. Ez ugye a szerver oldali validáció elbukása miatt van, úgyhogy változtassuk meg az assertSee()-s sort a következőre:

->assertSee('The utasneve must be at least 10 characters.');

Ez nem túl baráti, vagy magyar, inkább olyan "Hunglish kategória", de jelenleg még nem tartalmaz a szerver oldali validációs kódunk lefordított hibaüzenetet (ha valaki ennél barátságosabb hibaüzenetet szeretne írni, akkor érdemes áttekinteni az itt megtekinthető bejegyzésemet). Viszont, ha így futtatjuk a tesztelést, akkor OK eredményt kapunk, tehát a Dusk látja a szerver oldali validációs hibaüzenetet, emiatt megfelelő is lesz a tesztünk.


Összefoglalás

Ebben a bejegyzésben a validációs témakört zártam le (egy időre) úgy, hogy teszteléssel ellenőriztük egy űrlap kitöltését és a kliens- és szerver oldali szabályok betartatását. Adtam tanácsot azoknak, akik a tesztelés témakörében el szeretnének mélyebben merülni: tanulási (szakirodalom) és vizsgázási tanácsokat is megosztottam az olvasókkal. Tesztelési elmélet szempontjából átvettük, hogy az űrlap ellenőrzésénél milyen fajta érvényes és érvénytelen bemenetek lehetnek az egyes típusoknál, de szóba kerültek az tesztelési ekvivalencia osztályok, valamint a kód teszt lefedettsége is.

Írtam arról, hogy hogyan érdemes megtervezni egy tesztesetet, majd utána ezt három részletesebb példán is végigvezettem gyakorlati szempontból bemutatva. A példák során az "apróbb" finomságokra és bosszantóbb problémákra is felhívtam a figyelmet, hogy ezáltal segítselek benneteket a tesztesetek megfelelő definiálásánál. Főként a Laravel Dusk-ot használtam, ami egy elég hatékony eszköz a validációs szabályok ellenőrzésére is.

Ahogy korábban írtam, automatikus tesztet írni, készíteni is művészet. Meg kell tanulni, de rá is kell érezni, hogy hol vannak, lehetnek a hibák. Egy rossz teszt, rossz kódot is eredményezhet, úgyhogy ilyen esetben a tesztet is felül kell vizsgálni és lehet, hogy újra meg kell írni.

A bejegyzéshez tartozó Github commit itt található.