!

Dette materialet blir ikke lenger vedlikeholdt. Du vil finne oppdatert materiale på siden: http://borres.hiof.no/wep/

jsUnit
Testing
Børre Stenseth
Kodetesting >JStesting

Testing av Javascript

Hva
Hvordan teste Javascript

Når vi jobber med javascript er situasjonen litt anneledes enn når vi jobber med andre språk. Javascript er interpretert og vi har ingen gode utviklingsmiljøer. Vi skriver som regel kode i en editor som beste fall har fargekoding for syntaksen og vi tester ved å kjøre en HTML-side i en nettleser. Ofte er de feilene vi sliter med en kombinasjon av selve Javascriptkoden og det dokumentet (HTML-siden) koden arbeider mot. Det er to hjelpemidler som er lett til hånds:

  • De fleste nettleserne har en funksjon som gjør at vi lett finner syntaksfeil, unntak vi har glemt å fange opp, nullpekere osv. Min favoritt er FireFox sin Error Console, menyen Tools - Error Console.
  • Alert. Vi kan spore interpreteringen av koden ved å kalle alert i kritiske stadier, typisk: alert('Verdien til T. '+T);. Det er ganske masete å flytte slike alert-kall rundt i koden, men det kan være ganske effektivt.

Det finnes Unit-løsninger også for Javascript, slik som for Java, Python, .Net-språkene osv.. Vi skal se litt på en av disse i denne modulen.

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:

testrunner.gif

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.

HTML-siden, første utkast http://www.it.hiof.no/~borres/dw/jstest/sample20.html

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);
}
HTML-siden, ferdig http://www.it.hiof.no/~borres/dw/jstest/sample21.html
Referanser
  1. JSUnit JSUnit.net www.jsunit.net/ 14-03-2010
Vedlikehold

Børre Stenseth, juni 2007

( Velkommen ) Kodetesting >JStesting ( Autentisering )