Automatizace end-to-end testování je téma, které nás v Ackee zajímá. Zatímco weby úspěšně testujeme pomocí nástroje Cypress už nějaký ten pátek, pro mobilní aplikace se nám donedávna nedařilo nalézt ten správný nástroj. Pak jsme narazili na platformu TestProject.io, která by toto prázdné místo v našem stacku mohla zaplnit.
Jedná se o řešení, které umožňuje snadnou tvorbu a exekuci E2E testů jak pro mobilní, tak i webové aplikace. Zakládá si na jednoduchosti a podpoře komunity. Základní test totiž dokáže vytvořit kdokoliv i bez znalosti jakéhokoliv programovacího jazyka. Používání platformy je zdarma, komunita na oplátku vytváří různé doplňky, díky kterým se funkcionalita každým dnem rozšiřuje.
Ačkoliv TestProject umožňuje testovat i weby, my jsme jej zatím vyzkoušeli jen na mobilních aplikacích. O naše zkušenosti bych se rád podělil v tomto článku.
Tvorba testů
K samotné tvorbě testů lze v TestProject přistupovat několika způsoby. Tím prvním je použít vestavěný rekordér. Pomocí něj lze jednoduše nahrát test pouhým klikáním na jednotlivé elementy – přímo v náhledovém okně ve webovém prohlížeči. Jednotlivé kroky (kliknutí, swipe apod.) se zaznamenají a následně je lze zpětně přehrát. Tento přístup se nám ale příliš neosvědčil, protože si rekordér při každém kliknutí na konkrétní element dle svého uvážení vybere a uloží jeho selektor. Výsledkem je změť nepojmenovaných selektorů, které často nejsou zvoleny správně a sebemenší změna testované aplikace způsobí jejich nefunkčnost.
Pokročilejším přístupem je jednotlivé kroky ve webovém rozhraní TestProject vytvořit manuálně. Existují 3 typy kroků:
- Element action – provede akci s konkrétním elementem. Jednotlivé elementy lze vybírat pomocí selektorů podporovaných Appiem. Selektorů lze pro jeden element definovat několik a slouží jako fallback, pokud první selže. Jednou definované selektory pro daný element se uloží, a lze je proto využívat ve více krocích, není nutné je definovat pokaždé znovu. Mezi základní akce patří například kliknutí, kontrola viditelnosti, kontrola textu, získání velikosti, barvy a podobně. Další akce lze doinstalovat jako doplněk vytvořený komunitou. Ty jsou často specifické pro konkrétní platformu a umožňují testovat nativní systémové komponenty.
- Action – akce nezávislá na elementu. Může se jednat jak o akce, které přímo manipulují s danou aplikací – například swipe mezi konkrétními souřadnicemi, tak o akce, které s aplikací nijak neinteragují. Tímto způsobem lze tvořit i testy, které komunikují s nějakým API. Poměrně jednoduše tak lze získat nějaké hodnoty z API a následně je porovnat s hodnotami zobrazenými v aplikaci. I v tomto případě lze mnoho akcí doinstalovat jako doplněk vytvořený komunitou.
- Test – umožňuje vnořit do vytvářeného testu subtesty. Tato možnost je vhodná pokaždé, kdy je nutné vykonat nějakou souslednost kroků, která se opakuje v několika testech zároveň. Díky tomu se lze vyhnout zbytečné duplikaci těchto kroků tím, že je uložíme do samostatného subtestu, který následně vkládáme do hlavních testů.
V každém kroku lze nastavit chování při chybě – zda má celý test skončit chybou nebo se má v testu pokračovat. Případně lze spustit recovery test. Také lze nastavit, v jakém případě se pořídí snímek obrazovky. U každého kroku je možné specifikovat podmínku, která se ověří před jeho spuštěním. Pokud není podmínka splněna, krok se přeskočí. Samozřejmostí je možnost nastavit rychlost exekuce kroku a délku timeoutu.
Poslední a nejpokročilejší možností je napsat test pomocí kódu. Využívá se k tomu tzv. TestProject OpenSDK, které dědí veškeré příkazy známé ze Selenia a Appia. V současné době lze takový kód napsat v Javě, Pythonu nebo C#, ale brzy by měla přijít i dlouho slibovaná podpora JavaScriptu.
Z testů připravených ve webovém rozhraní lze automaticky vygenerovat testy psané pomocí kódu. To může sloužit jako odrazový můstek pro tvorbu složitějších testů, u nichž si základ naklikáme v grafickém rozhraní a zbytek dopíšeme přímo v kódu. Testy napsané pomocí SDK lze samozřejmě kombinovat s testy vytvořenými ve webovém rozhraní.
Automaticky vygenerovaný kód testu ze snímku obrazovky:
@DisplayName("Language filter")
@Test
void execute() throws Exception {
// set timeout for driver actions (similar to step timeout)
driver.manage().timeouts().implicitlyWait(15000, TimeUnit.MILLISECONDS);
By by;
boolean booleanResult;
iOSWheelPicker.iOSWheelPickerScrollTo iOSWheelPickerScrollTo;
// 1. Tap 'Filter content - Jazyk'
// Taps on the language filter button
GeneratedUtils.sleep(500);
by = By.xpath("//XCUIElementTypeOther/XCUIElementTypeStaticText[@label = 'Jazyk']");
(new TouchAction((driver))).tap(TapOptions.tapOptions().withElement(ElementOption.element((driver).findElement(by)))).perform();
// 2. Scroll picker wheel to 'previous' value until '{{language}}' is contained there
GeneratedUtils.sleep(500);
iOSWheelPickerScrollTo = iOSWheelPicker.iOSWheelPickerScrollTo("previous",language);
by = MobileBy.iOSNsPredicateString("type == 'XCUIElementTypePickerWheel' AND visible == 1");
iOSWheelPickerScrollTo = (iOSWheelPicker.IOSWheelPickerScrollTo)((ReportingDriver)driver).addons().execute(iOSWheelPickerScrollTo, by, -1);
// 3. Tap 'Picker wheel OK'
GeneratedUtils.sleep(500);
by = MobileBy.iOSNsPredicateString("type == 'XCUIElementTypeStaticText' AND label == 'OK' AND visible == 1");
(new TouchAction((driver))).tap(TapOptions.tapOptions().withElement(ElementOption.element((driver).findElement(by)))).perform();
// 4. Tap 'First table cell'
// Taps on the first filtered result
GeneratedUtils.sleep(500);
by = By.xpath("//XCUIElementTypeCell[1]");
(new TouchAction((driver))).tap(TapOptions.tapOptions().withElement(ElementOption.element((driver).findElement(by)))).perform();
// 5. Does 'Detail - language text' contain '{{language}}'?
// Selects text element that starts with "Jazyk:" and checks if contains {language}.
GeneratedUtils.sleep(500);
by = By.xpath("//XCUIElementTypeOther/XCUIElementTypeStaticText[starts-with(@label,\\"Jazyk:\\")]");
Assertions.assertTrue(driver.findElement(by).getText().contains(language));
}
Exekuce testů
Ačkoliv správa testů a reporting probíhá v cloudu, samotné testování se uskutečňuje lokálně. Testy lze spouštět jak na fyzických zařízeních, tak v simulátorech. Slouží k tomu tzv. TestProject Agent, který je jakousi nadstavbou nad Appiem a Seleniem. Ten se stará o komunikaci mezi zařízeními a simulátory v lokálním prostředí, a cloudovou platformou. K jednomu účtu lze připojit více agentů. Každý člen týmu si jej může nainstalovat do svého počítače a díky němu následně testy vytvářet a spouštět. Také si může zvolit, zda bude Agent v jeho zařízení soukromý nebo veřejný. Pokud si zvolí druhou možnost, Agent v jeho počítači bude sdílený se zbytkem týmu a ostatní členové budou moci využívat jeho fyzická zařízení a simulátory pro spouštění testů. V Ackee k tomuto účelu v současné době využíváme malý NUC, ve kterém běží několik simulátorů a je k němu připojeno několik telefonů.
Další možností je spouštět testy na zařízeních v cloudu pomocí služby BrowserStack nebo Sauce Labs.
Plánování provádění testů
Spuštění testů lze v TestProject naplánovat pomocí "Jobů". V jednom Jobu je jeden či více testů. Jednotlivým testům v Jobu lze předávat parametry z datového zdroje. To se může hodit například v případě, kdy chceme otestovat login s různými přístupovými údaji. Stačí vytvořit CSV soubor s jednotlivými údaji a test proběhne tolikrát, kolik má soubor řádků.
Po provedení Jobu se automaticky vytvoří report, který obsahuje i případné screenshoty a logy (záleží na nastavení konkrétních testů a jejich kroků).
Integrace
TestProject poskytuje REST API, pomocí kterého lze platformu napojit na CI/CD pipeline. V našem případě ho primárně používáme pro upload beta verzí aplikací po každém buildu. Díky tomu testujeme vždy jejich aktuální verzi. Přes API lze též spouštět Joby, získávat reporty o jejich provedení, upravovat datové zdroje, spravovat projekty a uživatele a podobně. Možností je mnoho.
Dále využíváme nativní integraci se Slackem, která nám po provedení Jobu odešle do kanálu jeho shrnutí.
TestProject 2.0 – „on premise“
Řešení, které jsem dosud popisoval, je zcela závislé na cloudové platformě, bez které nejde vytvořit ani spustit jediný test. V ideálním případě vše funguje jak má, ale ne vždy podle představ. V krajním případě by se také mohlo stát, že platforma skončí a my přijdeme o veškeré testy – přeci jen se jedná o řešení zdarma a takové věci se stávají. Tento aspekt nás dosud odrazoval od širšího nasazení na více projektech. Před pár dny ale byla představena nová verze, TestProject 2.0, která by náš problém měla řešit. Umožňuje spouštět testy kompletně offline, bez jakékoliv komunikace se servery TestProjectu.
Projekty a jejich testy lze z platformy stáhnout v podobě YAML souborů a následně je na Agentovi spouštět z příkazové řádky díky nově představenému CLI. Tyto testy lze snadno verzovat v GITu. Společně s celým balíkem se stáhnou i využité komunitní doplňky. Díky tomu by mělo být možné plně integrovat testy do CI/CL pipeline, aniž by bylo nutné komunikovat se servery třetí strany. Nepřišli bychom ani o reporty, ty se totiž automaticky generují ve formě HTML souborů.
Závěr
Služba TestProject.io nás zaujala svou jednoduchostí a rychlostí prvotního setupu. Bez nadsázky se dá říct, že i úplný začátečník bude mít vytvořený fungující test do 15 minut od registrace. Napomáhá tomu zejména SaaS koncepce celého řešení, díky které stačí nainstalovat jediný program a můžete tvořit. Tento přístup s sebou přináší i jisté nevýhody, z nichž většinu řeší poslední verze, která umožňuje spouštět testy offline. Veškeré možnosti nové verze jsme zatím detailně neprozkoumali, ale pravděpodobně se díky nim stane TestProject službou, se kterou budeme do budoucna počítat.