Testing av Javascript
JsUnit
Vi skal altså holde oss i "Unit"-tradisjonen og se på JsUnit for testing av Javascriptnkode. Merk at det er flere implementasjoner av xxUnit for Jacvascripttesting. Jeg holder meg til JsUnit [1] . Jeg har ikke grunnlag for å mene at denne er bedre enn andre. Det er bare å lete og prøve seg fram.
JsUnit må lastes ned og pakkes ut på din lokale maskin, eller den kan installeres på tjeneren. Vi holder oss til den første settingen her. Selve testingen foregår ved at vi laser opp en side i nettleseren og ber "siden" teste en side med Javascript som vi har skrevet. Testeren, testRunner.html, ser slik ut:
Utfordringen vår blir å lage de testsidene som vi kan laste opp og teste. En slik testside må bestå av:
- Inkludering av testkode
- Inkludering av vår egen kode som vi vil teste.
- Tester
Dokumentasjonssidene til JsUnit er vel ikke verdens beste, men med litt inspeksjon og prøving og feiling er det ganske kurant å sette opp enkle tester. Det er viktig å merke seg det dokumentasjonen sier om noen krav enkelte nettlesere stiller.
Et typisk eksempel på en testside kan se slik ut:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JsUnit test Sample0</title> <!-- JsUnit library --> <script language="JavaScript" type="text/javascript" src="file:///C:\jsunit\app\jsUnitCore.js"></script> <!-- my code --> <script language="JavaScript" type="text/javascript" src="myCode.js"></script> <!-- testing --> <script language="JavaScript" type="text/javascript"> function test1() {assert(true);} function test2() {assert(true);} </script> </head> <body> <h1>Test nothing</h1> <p>with JsUnit</p> </body> </html>
Eksempel 1
Vi lager et eksempel med et svært enkelt oppsett. Det som er viktig å merke seg er at funksjoner med navn som begynner på "test" blir automatisk kjørt av testRunner.
Utgangspunktet er følgende "kravspesifikasjon":
Vi ønsker å skrive en funksjon, convert, som konverterer et naturlig tall til en sekvens av tallord.
Vi begynner med å lage en testfunksjon:
function test1(){ assertEqual(convert(123),'en to tre'); assertEqual(convert(2.3),'ikke et naturlig tall'); assertEqual(convert('jensen'),'ikke et naturlig tall'); }
Første versjon av koden vår, convert, lager vi slik:
function convert(T){ return 'nonsens'; }
Vi kan da lage følgende testside:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>JsUnit test Sample0</title> <!-- JsUnit library --> <script language="JavaScript" type="text/javascript" src="file:///C:\jsunit\app\jsUnitCore.js"></script> <!-- my code with function convert --> <script language="JavaScript" type="text/javascript" src="convertCode.js"></script> <!-- testing --> <script language="JavaScript" type="text/javascript"> function test1(){ assertEquals("2.3",convert(2.3),"ikke et naturlig tall"); assertEquals("jensen",convert("jensen"),"ikke et naturlig tall"); assertEquals("123",convert(123),"en to tre"); } </script> </head> <body> <h1>Test number conversion</h1> <p>with JsUnit</p> </body> </html>
Denne feiler selvsagt, og testRunner gir følgende diagnose:
Tests with problems (1 total) - JsUnit ... 1. sample1.html:test1 failed Expected nonsens (string) but was en to tre (string) ...
Vi arbeider litt mer med funksjonen vår, convert. Vi tester og feiler og kommer etter hvert fram til følgende innhold i fila convertCode.js:
var numbers="0123456789"; var wds = new Array("null","en","to","tre","fire","fem","seks","sju","�tte","ni"); function convert(T){ var S=''+T; var res=''; for (ix=0;ix<S.length;ix++) { var pos =numbers.indexOf(S.substring(ix,ix+1)); if(pos==-1) return "ikke et naturlig tall"; res+=wds[pos]+' '; } return res.substring(0,res.length-1); }
Når vi tester denne får vi ingen feil.
Eksempel 2
I eksempelet ovenfor laged vi en testside som inkluderte Javascriptkoden vi ønsket å teste. I dette eksempelet utvider vi problemet slik at vi involverer samspillet mellom Javascriptkoden og HTML-siden.
Utgangspunktet er følgende "kravspesifikasjon":
Vi ønsker å lage en side der brukeren kan skrive inn naturlige heltall og få disse addert til en synlig sum.
Kildekoden er slik:
<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"> <head> <title>page to test</title> <!-- testcode. remove when tested --> <script language="JavaScript" type="text/javascript" src="file:///C:\jsunit\app\jsUnitCore.js"></script> <script language="JavaScript" type="text/javascript" src="testadder0.js"></script> <!-- end of testcode. remove when tested --> <script language="JavaScript" type="text/javascript" src="myAdder0.js"></script> </head> <body> <h1>Min sumside</h1> <p>Summen er: <span id="sum">0</span></p> <form action="#"> <div> Nytt tall: <input type="text" id="tall" name="tall" value="0" maxlength="3" size="4"/> <input type="button" value="Add" onclick="add(this.form.tall.value);return false;"/> </div> <div id="error" style="color:red"> </div> </form> </body> </html>
Der vår kode, myAdder.js, er slik:
function add(tallS){}
Jobben blir å skrive fila: testadder.js og funksjonen add().
Vi begynner med første utkast til test, testadder0.js. Vi lager den slik:
function exposeTestFunctionNames() { return ['test1']; } function test1() { document.getElementById('sum').innerHTML='1'; add("1"); assertEquals("2",document.getElementById('sum').innerHTML); }
Siden vi ikke har fylt funksjonen add() med noe fornuftig feiler testen:
Tests with problems (1 total) - JsUnit ... 1. sample20.html:test1 failed Expected 2 (string) but was 1 (string) ...
Vi lager en mer sofistikert test før vi begynner med å utvikle funksjonen add().
function exposeTestFunctionNames() { return ['test1','test2','test3','test4']; } function test1() { document.getElementById('sum').innerHTML='1'; add("1"); assertEquals("2",document.getElementById('sum').innerHTML); } function test2() { document.getElementById('sum').innerHTML='1'; add("jensen"); assertEquals("1",document.getElementById('sum').innerHTML); } function test3() { document.getElementById('sum').innerHTML='1'; add("2.3"); assertEquals("1",document.getElementById('sum').innerHTML); } function test4() { document.getElementById('sum').innerHTML='1'; add("2TT"); assertEquals("1",document.getElementById('sum').innerHTML); }
Så setter vi fokus på add()-funksjonen. Vi prøver oss med følgende:
function add(tallS) { var sumS=document.getElementById('sum').innerHTML; var sum=parseInt(sumS); var tall=parseInt(tallS); document.getElementById('error').innerHTML=''; if (isNaN(tall)) { document.getElementById('error').innerHTML='kun heltall'; return; } document.getElementById('sum').innerHTML=''+(sum+tall); }
test3 og test4 feiler siden parseInt() i Javascript tolker siffere "så lang den kan". Vi må altså plukke opp de situasjonene der inputteksten begynner på et siffer, men inneholder noe annet etterpå. Vi skriver om funksjonen add():
function add(tallS) { var sumS=document.getElementById('sum').innerHTML; var sum=parseInt(sumS); var tall=parseInt(tallS); document.getElementById('error').innerHTML=''; if (isNaN(tall)) { document.getElementById('error').innerHTML='kun heltall'; return; } for(ix=0;ix<tallS.length;ix++) if('0123456789'.indexOf(tallS.substring(ix,ix+1))==-1) { document.getElementById('error').innerHTML='kun heltall'; return; } document.getElementById('sum').innerHTML=''+(sum+tall); }