Testování Javascriptových projektů II
od aichi
V minulém díle jsme si nakonfigurovali Javascriptový framework JSUnit a vytvořili první jednoduché testy. Dnes se podíváme na to jak nastavit testovací prostředí a jak využít zabudované mock objekty pro testy asynchronních funkcí.
...
Příprava testovacího prostředí a úklid
V našem prvním testu jQuery jsme si připravili testovací data přímo v testovací funkci. Toto je možné, ale pokud více funkcí potřebuje stejné startovací podmínky, je vhodné tuto přípravu vyčlenit do zvláštní funkce. JSUnit proto zavádí funkci setUp.
V této funkci můžeme připravit data a víme, že framework ji spustí před každým testem. Navíc je vhodné po každém testu i uklidit a proto je zavedena funkce tearDown. V testovacím frameworku není nikdy dáno v jakém pořadí budou testovací funkce spuštěny, proto nelze spoléhat na to, jaká data jeden test vytvoří, v testu jiném.
Náš kód pro test jQuery můžeme upravit takto:
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script language="JavaScript" src="../jsunit/app/jsUnitCore.js"></script>
<script language="JavaScript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script language="JavaScript">
var div;
function setUp() {
div = document.createElement('div');
div.id = 'testDiv';
div.name = 'abc';
document.body.appendChild(div);
};
function tearDown(){
document.body.removeChild(div);
delete div;
};
function testGettingElement() {
var jdiv = $("#testDiv");
assertNotNull(jdiv);
assertEquals(jdiv[0], div);
var jdiv2 = $("#div");
assertNotNull('jQuery objekt musí existovat',jdiv2);
assertEquals('jQuery objekt nesmí obsahovat žádný element',jdiv2[0], null);
};
function testGettingAttribute(){
var jdiv = $("#testDiv");
assertEquals(jdiv.attr("name"), div.name);
//zmena parametru behem testu
div.name = 'xyz';
assertEquals(jdiv.attr("name"), div.name);
};
</script>
</head>
<body>
</body>
</html>
Testování asynchronních funkcí
Pokud potřebujeme získat data ze serveru pro testy a nejde z nějakého důvodu tyto data nasimulovat, je vhodné je získat pouze jednou před vlastním spuštěním testů. K tomu slouží funkce setUpPage.
Pokud je tato funkce přítomna, je spuštěna před funkcí setUp i před vlastními testy. Tyto funkce nejsou spuštěny dokud tato funkce nenastaví hodnotu proměnné setUpPageStatus na hodnotu "complete".
<html>
<head>
<script language="JavaScript" src="../jsunit/app/jsUnitCore.js"></script>
<script language="JavaScript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.4/jquery.min.js"></script>
<script language="JavaScript">
function setUpPage() {
//simulace ziskani dat
setTimeout(setUpPageEnd, 10000);
};
function setUpPageEnd() {
setUpPageStatus = 'complete';
};
function setUp() {
//...
};
function tearDown(){
//...
};
function testSomething() {
//...
};
</script>
</head>
<body>
</body>
</html>
Můžeme si všimnout, že v běhovém prostředí je kolonka Setup page timeout s výhozí hodnotou 120. Tato hodnota značí, že framework čeká max. 2 minuty na nastavení této proměnné na hodnotu complete. Pokud proměnná nebude nastavena do časového limitu, běhové prostředí vyhodí chybu.
Clock mock
Takto jde otestovat získání dat ze serveru, ale jak testovat asynchronní funkce, které jsou volány pomocí setTimeout nebo setInterval? Představme si funkci, která po zavolání nastaví za 1s předanému elementu třídu (velice zjednodušená animace):
function indentText(elm) {
setTimeout(function(){elm.className = 'indent'}, 1000);
};
Jak jí otestujeme? JSUnit propaguje objekt Clock, který zpozdí vykonávání kódu, který je zapsán po něm. Testovací funkce k otestování správné funkčnosti funkce indentText výše může vypadat takto:
<html>
<head>
<script language="JavaScript" src="../jsunit/app/jsUnitCore.js"></script>
<script language="JavaScript" src="../jsunit/lib/jsUnitMockTimeout.js"></script>
<script language="JavaScript">
function indentText(elm) {
setTimeout(function(){elm.className = 'indent'}, 1000);
};
function testIndentText(elm) {
var elm = document.createElement("div");
indentText(elm);
//po 500ms trida nesmi byt nastavena
Clock.tick(500);
assertNotEquals(elm.className, 'indent');
//po 1s trida musi byt nastavena
Clock.tick(1000);
assertEquals(elm.className, 'indent');
};
</script>
</head>
<body>
</body>
</html>
Je na čase si rozebrat jak tento kód funguje. Na začátku je připojena knihovna pro testování asynchronního kódu. Tato knihovna přepíše funkce setTimeout, setInterval, clearTimeout a clearInterval a nahradí je vlastní funkčností. Ve skutečnosti je funkce, která má být vykonána uložena a jakmmile zavoláme metodu Clock.tick posuneme virtuální čas a všechny uložené metody, které mají reagovat při násobku virtuálního času jsou vykonány. Pak je možné zavolat testovací assert funkce. Řešení je velice jednoduché a funkční.
Testování výstupu serveru bez nutnosti se ho dotazovat
Při psaní testu bychom se měli vyvarovat použití dat z reálného serveru, neboť výsledek testu bude záviset na tom, zda služba na serveru běží a vrací správná data. Místo toho je lepší mít testovací data předem připravená. Představme si službu, která se dotazuje serveru na aktuální kurzy. Je asi nevhodné testovat data z reálného serveru, neboť kurzy se mění každý pracovní den.
Bohužel pro tento případ není v JSUnit připraven mock objekt. Je zde sice knihovna pro vytvoření mock XMLHTTPRequestu, ale není univerzálně zaměnitelná za nativní objekt a její využití je spíše sporadické.
<html>
<head>
<script language="JavaScript" src="../jsunit/app/jsUnitCore.js"></script>
<script language="JavaScript" src="../jsunit/lib/jsUnitAjax.js"></script>
<script language="JavaScript">
var request;
function setUp() {
request = createXmlHttpRequest();
}
function testOpenRequest() {
request.open("GET", "/kurzy.txt", true);
assertEquals("GET", request.method);
assertEquals("/kurzy.txt", request.url);
assertTrue(request.isAsync);
}
</script>
</head>
<body>
</body>
</html>
Automatizace
Nejlepším testovacím nástrojem je ten, který lze spouštět automaticky, např. při každém commitu do repozitáře verzovacího systému.
JSUnit podporuje automatické spuštění z příkazové řádky pomocí např. takovéto URL: http://localhost:8000/jsunit/testRunner.html?testPage=localhost:8000/tests/test.html&autoRun=true. Nicméně pro automatické testování je toto nedostatečné, protože potřebujeme také získat výsledky testu k dalšímu zpracování. Pro tuto příležitost je s JSUnit distribuován jednoduchý Java server, který je nejvhodnější spustit jako ant proces. Ruční spustění je možné příkazem java -cp lib/*.jar;lib/*/*.jar;bin/jsunit.jar net.jsunit.JsUnitServer z adresáře java.
Závěr
Dnes jsme vyčerpali možnosti JSUnit. Je vhodný především pro testování Javascriptové funkcionality nezávislé na serverových datech. Příště se podíváme na framework Jasmine. Vyzkoušíme si pokročilé mockování a přepíšeme použité příklady pro JSUnit do kompatibilní podoby.
Adresy zpětných odkazů pro tento příspěvek:
Trackback URL (right click and copy shortcut/link location)
01. 03. 11 00.09:00, 
