Møt Crockfords JSCheck

Det finnes dusinvis av JavaScript-testrammer, men de fleste fungerer i mer eller mindre på samme måte. Douglas Crockfords JSCheck er imidlertid betydelig forskjellig fra de fleste. I denne veiledningen vil jeg vise deg hvordan det er annerledes og hvorfor du bør vurdere å bruke den!


Crockford beskriver JSCheck som et "spesifikasjonsdrevet testverktøy.

Crockford beskriver JSCheck som et "spesifikasjonsdrevet testverktøy". Når du bruker rammene du er vant til, vil du skrive en test for et gitt funksjonalitet, og hvis testen overgår, erklærer du at funksjonen fungerer som den skal . Det er imidlertid mulig at du kanskje savner noen av kantsaker eller unntak som dine tester ikke dekker.

Mens avdekkende kasser er ikke det eksplisitte formål med JSCheck, er det en fin sidefordel. Hovedideen bak JSCheck er dette: Spesifikasjonen du skriver, vil faktisk beskrive hvordan koden du tester skal fungere. Deretter vil JSCheck ta den spesifikasjonen (kalt a krav i JSCheck-lingo), og generere tilfeldige tester for å bevise kravet. Endelig vil det rapportere resultatene til deg.

Høres interessant ut? Les videre! Høres kjent ut? Du har kanskje brukt Haskell testverktøy, QuickCheck, som JSCheck var basert på.


Noen kode som skal testes

Selvfølgelig, før du faktisk skriver vårt krav, vil vi ha noen kode som skal testes. Nylig skrev jeg en mini-passord scorer, ligner på funksjonaliteten på HowSecureIsMyPassword.net. Det er egentlig ikke fancy: du sender bare passordet et passord og får en poeng tilbake. Her er koden:

passwordScorer.js

(funksjon ) var PasswordScorer = ; PasswordScorer.score = funksjon (passord) var len = passord.lengde, lengdeScore = 0, letterScore = 0, chars =  hvis (len> = 21) lengthScore = 7 ; ellers hvis (len> = 16) lengthScore = 6; annet hvis (len> = 13) lengdesekvens = 5; annet hvis (len> = 10) 8) lengthScore = 3; ellers hvis (len> = 5) lengthScore = 2; var re = [null, / [az] / g, / [AZ] / g, / \ d / g, / [ ! @ # $% \ ^ & \ * \ (\) = _ + -] / g]; for (var i = 1; i < re.length; i++)  letterScore += (password.match(re[i]) || []).length * i;  return letterScore + lengthScore; ; (typeof window !== 'undefined' ? window : exports).PasswordScorer = PasswordScorer; ());

Det er ganske enkelt kode, men her er det som skjer: poengsummen består av to delpoeng. Det er en startpoengsum, basert på lengden på passordet, og deretter en ekstra poengsum for hvert tegn, 1 poeng for hvert små bokstaver, 2 poeng for hver stor bokstav, 3 poeng for hvert tall og 4 poeng for hvert symbol ( fra et begrenset sett).

Så dette er koden vi skal teste: vi vil tilfeldigvis generere noen passord med JSCheck og sørge for at de får en passende poengsum.


Skrive kravet vårt

Nå er vi klare til å skrive våre krav. Først, hodet over JSCheck Github siden og last ned jscheck.js fil. Jeg liker å kjøre testene mine i terminalen, via NodeJS, så legg til denne linjen helt til bunnen av filen:

(type av vindu! == 'undefined'? vindu: eksport) .JSC = JSC;

Dette vil ikke påvirke måten filen oppfører seg i nettleseren, men det vil få det til å fungere som en modul i Node. Legg merke til at jscheck.js filen utsettes JSC som den eneste globale variabelen for hele biblioteket. Hvis vi ikke gjorde denne tilpasningen, så har vi tilgang til den.

La oss åpne passwordScorerSpec.js og start ting:

JSC = krever ("./ ... /vendor/jschec";).JSC; PasswordScorer = krever ("./ ... /lib/passwordScore";).PasswordScorer;

Siden jeg kjører disse testene i NodeJS, må vi kreve modulene vi vil ha. Selvfølgelig vil du sørge for at stier samsvarer med filplasseringene dine.

Nå er vi klare til å skrive vår første påstand. Selvfølgelig bruker vi JSC.claim metode. Denne metoden aksepterer tre parametere, med en valgfri fjerde. Den første parameteren er bare en streng, a Navn for kravet. Den andre parameteren kalles predikat: Det er den faktiske testfunksjonen. Svært enkelt, denne funksjonen bør returnere ekte hvis kravet er sant, og falsk hvis kravet er feil. De tilfeldige verdiene som JSCheck genererer for testen, vil bli sendt som parametere til predikatet.

Men hvordan vet JSCheck hvilken type tilfeldige verdier som skal håndtere predikatet? Det er her den tredje parameteren, den Specifier kommer inn i spill. Dette er en matrise, med et element for hver parameter for predikat. Elementene i arrayet angir hvilke typer som skal gi predikatet, ved hjelp av JSChecks spesifikke funksjoner. Her er noen av dem:

  • JSC.boolean () returnerer enten true eller false.
  • JSC.character () tar en min og maks karakter og returnerer en enkelt karakter fra dette området. Det kan også ta en enkelt tegnkode og returnere det tegnet.
  • JSC.integer () vil returnere et hovednummer. Eller send det til en enkelt parameter for å få et heltall (hele tall) mellom 1 og parameteren, eller to parametere for et heltall i det området.

Du får ideen. Det er andre spesifikatorer, og vi bruker noen nå som vi skriver vår første påstand.

JSC.claim ("All Lowercase Password" ;, funksjon (passord, maksScore) return PasswordScorer.score (passord) <= maxScore; , [ JSC.string(JSC.integer(10, 20), JSC.character('a', 'z')), JSC.literal(26) ]);

Vår første parameter er et navn. Den andre er testfunksjonen: den mottar et passord og en maks poeng, og returnerer sant hvis poengsummen for det passordet er mindre enn eller lik maksimalpoengsummen. Deretter har vi vårt spesifikke utvalg. Vår første parameter (passordet) skal være en streng, så vi bruker JSC.string () Metode: Det kan ta to parametere, antall tegn i strengen, og verdien for disse tegnene. Som du kan se, ber vi om et passord mellom 10 og 20 tegn. For verdien bruker vi JSC.characters () metode for å få tilfeldige tegn mellom 'a' og 'z'.

Den neste verdien er vår maxScore parameter. Noen ganger vil vi ikke ha tilfeldigheten som JSCheck tilbyr, og dette er en av disse tider. Det er derfor det er JSC.literal: å overføre en bokstavelig verdi til predikatet. I dette tilfellet bruker vi 26, som bør være maks poengsum for alle små bokstaver mellom 10 og 20 tegn.

Nå er vi klare til å kjøre testen.


Kjører kravet vårt

Før vi faktisk driver kravet og får rapporten, må vi sette opp funksjonen som vil motta rapporten. JSCheck sender rapporten til en tilbakeringingsfunksjon av JSC.on_report. Derfor:

JSC.on_report (funksjon (str) console.log (str););

Ikke noe spesielt. Nå er alt som er igjen å ringe JSC.check (). Nå kan vi gå til terminalen vår og kjøre dette:

nodepath / til / passordScorerSpec.js

Bak kulissene kjører JSCheck predikatet 100 ganger, genererer forskjellige tilfeldige verdier hver gang. Du bør se rapporten skrevet ut.

Alle små bokstaver 100 av 100 passerer 100

De passerte alle, men det er ikke mye av en rapport, eh? Vel, hvis noen av testene våre hadde mislyktes, ville de ha blitt inkludert i rapporten. Du kan imidlertid justere utgangsnivået med JSC.detail funksjon: send det et tall mellom 0 og 4 (inkluderende) for å få noe uten utgang til alle testtilfeller. Standardverdien er 3.


Legge til en klassifikator

Husk hvordan jeg sa det JSC.claim kan ta en fjerde parameter? Det kalles a klassifikator, og den mottar de samme parametrene som predikatet mottar. Deretter kan den returnere en streng for å klassifisere eller gruppere våre testtilfeller. Jeg innrømmer at jeg ikke var helt sikker på hvor dette ville være nyttig før jeg opprettet det ovennevnte eksempelkravet. Se, jeg gjorde en feil i predikatet og sammenlignet poengsummen til maxScore med operatør i stedet for operatør, slik at alle passord som gjorde 26 poeng var sviktende. Jeg så på rapporter som så ut som dette:

Alle små bokstaver Passord 96 av 100 FAIL [12] ("vqfqkqqbwkdjrvplkrx";, 26) FAIL [21] ("nhgkznldvoenhqqlfza"; 26) FALD [62] ("eclloekuqhvnsyyuekj"; 26) FAIL [78] ("rvrkfivwtdphrhjrjis" ;, 26) pass 96 svikt 4

Det er fortsatt ikke helt klart hvorfor noen tester svikter. Så jeg la til en klassifiseringsfunksjon som grupperte test sakene etter score: Som jeg sa, tar funksjonen de samme parametrene som predikatet, og det returnerer en streng. Hver test sak som får samme streng tilbake fra klassifiseringen, blir gruppert sammen i rapporten.

funksjon (passord, maxScore) return PasswordScorer.score (passord) + "poeng" ;; 

Denne funksjonen bør være den siste parameteren i vårt krav. Nå får du en rapport som er noe slikt:

Alle små bokstaver 96 av 100 feil [4] 26 poeng :( "illqbtiubsmrhxdwjfo"; 26) feil [22] 26 poeng :( "gruvmmqjzqlcyaozgfh"; 26) feil [34] 26 poeng :( "chhbevwtjvslprqczjg" ;, 26 ) FELL [65] 26 poeng: ("kskqdjhtonybvfewdjm";, 26) 14 poeng: bestå 8 15 poeng: bestå 5 16 poeng: bestå 12 18 poeng: bestå 10 19 poeng: pass 12 20 poeng: pass 11 22 poeng 12 23 poeng: bestå 8 24 poeng: pass 10 25 poeng: pass 8 26 poeng: pass 0 mislykkes 4

Du kan se hvordan testene grupperes med hvor mange poeng passordene er verdt. Nå er det enkelt å se at de eneste passordene som feiler tester, er passordene som gir 26 poeng. Og mens problemet her var med testen, og ikke koden, viser det fortsatt hvordan det kan være nyttig å legge til en klassifiseringsfunksjon for dine krav.


Siste tanker

Så, på slutten av dagen, er det JSCheck verdt å bruke? Her er det jeg tror: Det er ikke noe du nødvendigvis skal bruke med alle kodebaser, men noen ganger vil du finne det nyttig å kunne lage tilfeldige testtilfeller som vil teste et bestemt stykke kode. Når det er det du vil gjøre, har jeg ikke sett et verktøy bedre for det enn JSCheck.

JSCheck har noen andre alternativer og en rekke spesifikatorer som vi ikke har vurdert i denne opplæringen; Gå over til JSCheck.og for å lese om dem. Ellers vil jeg gjerne høre dine tanker om JSCheck i kommentarene!