Třetí z minisérie článků popisujících zkušenosti získané při testování webové aplikace pomocí automatických funkcionálních testů. Seznámíte se s podrobnostmi testování, dobrými radami a celkovým shrnutím.
Třetí z minisérie článků popisujících zkušenosti získané při testování webové aplikace pomocí automatických funkcionálních testů. Seznámíte se s podrobnostmi testování, dobrými radami a celkovým shrnutím.
V předchozím článku byla stručně charakterizována testovaná aplikace a věřím, že si ji někteří čtenáři i vyzkoušeli.
Jak již bylo zmíněno v prvním dílu, používal jsem pro testování Selenium WebDriver (jehož verze se průběžně měnily, poslední použitá byla 3.14). Testy jsem psal v Javě v Eclipse a využíval jsem hojně možností frameworku JUnit 4.
Hned na začátku je nutné zmínit problematiku organizace testů. Dopředu bylo jasné, že testů bude mnoho a mým cílem bylo umožnit jednoduše spouštění všech testů najednou, jejich skupin, podskupin skupin atd. až do úrovně jednotlivých testovacích tříd. Případně při vývoji testů i jednotlivých testovacích případů, což IDE Eclipse umožňuje. Kromě toho se ukázalo jako výhodné vytvářet i skupiny testů napříč hierarchií, např. všechny testy modálních oken. Pro účely organizace testů dává JUnit4 velmi dobré možnosti díky anotaci @Suite.
@RunWith(Suite.class) @Suite.SuiteClasses({ All_Passive_Tests.class, All_Active_Tests.class, All_Negative_Tests.class, })
Celou sadu funkcionálních testů lze rozdělit na dvě části. V první části (balík support
) jsou všechny pomocné třídy, které budou testy využívat. Většinou mají formu knihovních tříd, tj. pouze statické metody nebo konstanty. Po mnoha refaktorizacích je základní struktura balíku support
následující:
basic
– nejzákladnější soubory se seznamy ID a pro zpracování konfigurace
db
– sestava tří podbalíků s celkově 14 třídami. Tento balík víceméně supluje funkčnost databázové vrstvy testované webové aplikace. Tento přístup by se dal označit jako varianta N-version programming.
test
– pomocné třídy a rozhraní, zejména pro kategorizaci testů. To pak umožní díky anotaci @IncludeCategory
spouštět jen vybrané podskupiny (kategorie) testů
@RunWith(Categories.class) @IncludeCategory(ModalContentTest_Category.class) @SuiteClasses(All_Tests.class)
utils
– nástroje pro práci s webem rozšiřující možnosti Selenia WebDriver. Má dva podbalíky:
web
– třídy pro práci se základními elementy webové aplikace, např. nalezení tlačítka podle ID, kliknutí na něj a čekání na odezvuapplication
– vysokoúrovňové aktivity přímo na míru testované aplikace, např. „v tabulce zapsaných předmětů zruš předmět daného jména“Díky kvalitní funkcionalitě tříd z balíku utils
lze pak psát velmi jednoduše a přehledně vlastní testy.
Poznámka: Některé třídy z balíků db
a utils
jsou otestovány jednotkovými testy, což se ukázalo jako výhodné. Tyto testy nebudou dále zmiňovány a nejsou započítávány do statistik žádných testů.
Druhá část sady funkcionálních testů jsou již vlastní testy. Ty lze rozdělit do tří základních skupin, které jsou uloženy ve stejnojmenných balících:
1. passive
– testy zjišťující, zda je vše nastaveno, tak jak je očekáváno. Žádný z těchto testů nemění stav testované aplikace. Pomocí kategorií testů se dají dále dělit na (seřazeno podle časové náročnosti testů):
Po průchodu všech pasivních testů si můžeme být jisti, že počáteční stav aplikace je bezchybný. Pokud by bylo možné použít termín „pokrytí testy“, pak zde je 100%.
2. active
– testy, kdy je měněn stav testované aplikace. Dají se dělit do tří skupin:
Byly prověřeny všechny jednotlivé aktivity a k nim všechny jejich známé konsekvence.
Byly prověřeny všechny jednotlivé aktivity a k nim všechny jejich známé konsekvence.
Byly prověřeny nejběžnější scénáře. Všechny možné kombinace nelze samozřejmě provést staticky napsanými testy. Zde je příležitost pro dynamicky generované testy.
3. negative
– negativní testy (testy selháním). Protože UIS poskytuje jen velmi omezené možnosti uživatelských vstupů, kdy lze zadat libovolný údaj, je těchto testů jen omezený počet. Testována byla opět všechna známá potenciální rizika.
Rozsahy testů i podporujícího kódu jsou zřejmé z následující tabulky. Významy jednotlivých sloupců jsou stejné, jako u dříve uvedené tabulky. Poslední sloupeček udává počet testů, tj. porovnání dvou veličin (assertů).
celkově řádek | řádek kódu | testů | |
---|---|---|---|
support |
4 991 | 3 233 | – |
passive |
5 214 | 3 282 | 456 |
active |
4 800 | 3 146 | 547 |
negative |
1 111 | 780 | 63 |
dohromady | 16 116 | 10 441 | 1 066 |
Otestovat zodpovědně i jednoduchou aplikaci je opravdu náročné. Když ale programátoři vytvoří lépe testovatelný kód, je to o něco snazší (viz dále).
Tou je snadná a rychlá opakovatelnost testů a její význam je dán realitou webových prohlížečů. Desktopovou aplikaci prakticky stačí otestovat pouze jednou a – pokud použiji mírnou nadsázku – dokud se nezmění operační systém, bude desktopová aplikace fungovat pořád stejně. Operační systémy se nemění příliš často.
Webových prohlížečů je ale mnoho typů, minimálně pět až šest těch nejdůležitějších. Už jen možnost otestovat automaticky aplikaci podle jednoho kódu testů na všech prohlížečích je životně důležitá. Navíc – a to je nutné zdůraznit – verze prohlížečů se mění velmi rychle a uživatel má často nastaven automatický upgrade. Čili webová aplikace, která uživateli včera večer v jeho oblíbeném prohlížeči fungovala, už druhý den ráno fungovat nemusí. Rychlost postřehnutí tohoto problému je pro provozovatele aplikace důležitá.
Protože Selenium WebDriver využívá skutečný prohlížeč, lze velmi snadno (za cenu strojového času) ověřit, že konkrétně v něm funguje vše, jak má. A to opakovat pro další typ prohlížeče. A relativně snadno by bylo možné připravit nadstavbu nad testy, které by pak byly opakovaně spouštěny pro různé verze prohlížečů. S příchodem nové verze prohlížeče to pak pouze znamená získat pro něj novou verzi driveru (což někdy není nutné, protože drivery fungují pro více verzí) a spustit sadu automatizovaných testů. Čili zjištění, že aplikace bude korektně fungovat v právě nové verzi prohlížeče, je otázka chvíle příprav a pak doby běhu automatických testů.
Další výhodou automatizovaných testů jsou situace, kdy (téměř) nelze použít manuální testování. To je typicky u výkonnostních či zátěžových testů. (Zde upřímně přiznávám, že jsem žádné výkonnostní testování neprováděl, takže pouze teoretizuji.) Výkonnostní testy by měly být provedeny nejen při prvním release, ale pak i při každém dalším. Manuálně se toto provádí a měří těžko a je to drahé, automatizací můžeme testy kdykoliv zadarmo pustit znovu a s mnohem větší přesností dosažených výsledů.
Tester, který píše automatizované testy, odvádí principiálně stejně kvalifikovanou práci jako vývojář. Ten mu (jako kolegovi) může výrazně pomoci tím, že napíše svůj kód testovatelný. Naprosté minimum je dodat každému elementu jeho unikátní ID (případně – tam kde není ID možno použít, např. u Vaadin – pak jiný způsob pro jednoznačnou identifikaci). ID by měla být v celé aplikaci tvořena podle jednotného schématu. Pokud jsou v testech adresovány elementy pomocí jejich ID, výrazně to sníží „křehkost“ těchto testů, tj. při dílčích změnách layoutu není třeba testy měnit buď vůbec, nebo jen velmi málo.
Dále vývojář rozhodně testera (a nejen jeho) potěší tím, že stejné věci budou nejen navenek vypadat stejně, ale i vnitřně (tj. HTML kód) budou stejné. Například všechny tabulky budou mít záhlaví pomocí <th>
, což se může mnoha lidem jevit jako samozřejmé, nebo všechna modální okna bude možné uzavřít pomocí jednotně identifikovaného elementu.
Mít konfigurační soubor, kde budou minimálně tři věci.
#URL_Base=http://localhost:8080/uis URL_Base=http://oks.kiv.zcu.cz:10008 WebBrowserType=Chrome #WebBrowserType=Opera #WebBrowserType=Mozilla ChromeDriverURI=c:/Program Files/Java/selenium/chromedriver2-44.exe OperaDriverURI=c:/Program Files/Java/selenium/operadriver2-40.exe MozillaDriverURI=c:/Program Files/Java/selenium/geckodriver0-23-0.exe
Webové prohlížeče totiž mají většinou automatizované aktualizace, ale jejich seleniové drivery samozřejmě nikoliv. Takže pokud vám najednou přestane všechno fungovat, máte možnost jednoduchou změnou spustit funkcionální testy pomocí jiného prohlížeče. A je velmi nepravděpodobné, že by se oba prohlížeče současně aktualizovaly tak, že „vypadnou“ z rozsahu podporovaného příslušným driverem. Konkrétně já jsem používal prohlížeče Chrome (primárně), Firefox a Opera. Od března 2018 do listopadu 2018 se driver pro Chrome změnil osmkrát (verze 2–37 až 2–44).
Připravte si (abstraktní) třídu, od které budou všechny testy dědit. Úkolem této třídy je načtení konfigurace (viz předchozí bod), tj. inicializace příslušného webového prohlížeče v Seleniu. Toto je časově náročná operace a měla by tedy proběhnout pouze jednou, ať již spouštíte jeden test nebo skupinu testů. A na konci by měla příslušný ovladač (potažmo webový prohlížeč) standardně ukončit.
Aby bylo možné spouštět testy jednotlivě i po skupinách, využijte z JUnit4 anotace @Category
a @Suite.SuiteClasses
. Možností volit granulitu spouštěných testů výrazně zrychlíte jejich ladění.
Jakmile máte zpracovanou konfiguraci a granulitu testů a celé je to ověřené na jednoduchých testech, nepouštějte se do psaní dalších testů. Místo toho se snažte napsat co nejuniverzálnější podpůrný kód k budoucím testům (balík support
). Použitím kvalitního support
výrazně zrychlíte následné psaní skutečných testů a zpřehledníte je. A pokud ještě kód support
otestujete jednotkovými testy, bude to skvělé, protože při refaktoringu, ke kterému určitě dojde, využijete výhod těchto regresních testů.
Parametrizované testy (anotace JUnit4 @Parameters
) jsou významným pomocníkem pro zkrácení a zpřehlednění kódu.
Vlastní provádění testů můžete značně zrychlit tím, že použijete možnost definovat pořadí testů ( @FixMethodOrder
v JUnit4. Zde pozor na JUnit 5.0, která tuto možnost nemá – přichází plnohodnotně až v JUnit 5.4.). Psát testy, které jsou závislé na sobě navzájem (tj. které musejí běžet v určitém pořadí), není obecně dobrý nápad. Ale když se uváží situace, kdy se provede u studenta jedna aktivita a vzápětí se kontroluje její dopad na všech možných místech, pak je rozhodně rychlejší, zkontrolovat vše potřebné najednou u již přihlášeného studenta a pak najednou u následně jednou přihlášeného učitele. Střídavé přihlašování a odhlašování studenta a učitele při náhodném pořadí testů je zbytečné a podstatně zdržuje.
Chrome je možné spustit v headless módu. Podle mých pokusů to ale čas průběhu testů příliš nezkrátí (1836 sec oproti 1800 sec v headless módu).
Protože elementů typu input
, kdy uživatel může zadat libovolný údaj, je v aplikaci minimum, je také zapotřebí omezené množství negativních funkcionálních testů. Celkově jich bylo pouze 6 %.
support
) je třeba začít již od samého začátku. Zlepší to i strukturu vyvíjené aplikace.support
(viz též výše), jsou GUI testy až překvapivě stabilní. To jsem prakticky vyzkoušel na přidání jedné funkcionality do učitelova menu.db
). Tuto druhou aplikaci je sice možné testovat (minimálně jednotkovými testy), ale pravděpodobně to málokdo udělá. Jediným důkazem korektnosti je tedy využití již zmíněného principu N-version programming, kdy soulad testovací a testované aplikace poskytuje přiměřenou míru jistoty správnosti obou.Na úplný závěr bych rád dodal, že uvítám praktické rady zkušenějších, které bych pak promítl do výuky. Na druhou stranu jsem ochoten poradit těm méně zkušeným.
Pracuje na Katedře informatiky a výpočetní techniky Fakulty aplikovaných věd na Západočeské univerzitě v Plzni, zabývá se programovacími jazyky, softwarovými technologiemi a testováním.
Internet Info Root.cz (www.root.cz)
Informace nejen ze světa Linuxu. ISSN 1212-8309
Copyright © 1998 – 2019 Internet Info, s.r.o. Všechna práva vyhrazena.