Maak kennis met de JSCheck van Crockford

Er zijn tientallen JavaScript-testframeworks, maar de meeste werken min of meer op dezelfde manier. De JSCheck van Douglas Crockford verschilt echter aanzienlijk van de meeste. In deze zelfstudie laat ik u zien hoe het anders is en waarom u zou moeten overwegen om het te gebruiken!


Crockford beschrijft JSCheck als een "specificatie-gedreven testtool.

Crockford beschrijft JSCheck als een "specificatiegestuurde testtool." Wanneer u de frameworks gebruikt die u gewend bent, schrijft u een test voor een bepaald stuk functionaliteit en, als die test slaagt, verklaart u dat de gegeven functionaliteit correct werkt . Het is echter mogelijk dat u sommige randgevallen of uitzonderingen mist die niet door uw tests worden gedekt.

Hoewel het onthullen van edge cases niet het uitdrukkelijke doel is van JSCheck, is het een mooi bijkomend voordeel. Het belangrijkste idee achter JSCheck is dit: de specificatie die u schrijft, zal eigenlijk beschrijven hoe de code die u test zou moeten werken. Dan zal JSCheck die specificatie nemen (genaamd a vordering in JSCheck-lingo), en genereer willekeurige tests om de claim te bewijzen. Ten slotte zal het de resultaten aan u rapporteren.

Klinkt interessant? Lees verder! Klinkt bekend? Mogelijk hebt u de Haskell-testtool QuickCheck gebruikt, waarop JSCheck was gebaseerd.


Sommige code om te testen

Voordat we onze claim daadwerkelijk schrijven, willen we natuurlijk eerst een code laten testen. Onlangs heb ik een mini-wachtwoordscorer geschreven, vergelijkbaar met de functionaliteit op HowSecureIsMyPassword.net. Het is echt geen luxe: je geeft de functie gewoon een wachtwoord door en krijgt een score terug. Hier is de code:

passwordScorer.js

(function () var PasswordScorer = ; PasswordScorer.score = function (password) var len = password.length, lengthScore = 0, letterScore = 0, chars =  if (len> = 21) lengthScore = 7 ; else if (len> = 16) lengthScore = 6; else if (len> = 13) lengthScore = 5; else if (len> = 10) lengthScore = 4; else if (len> = 8) lengthScore = 3; else if (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; ());

Het is een vrij simpele code, maar hier is wat er aan de hand is: de score bestaat uit twee sub-scores. Er is een startscore, die is gebaseerd op de lengte van het wachtwoord en vervolgens een extra score voor elk teken, 1 punt voor elke kleine letter, 2 punten voor elke hoofdletter, 3 punten voor elk cijfer en 4 punten voor elk symbool ( van een beperkte reeks).

Dit is dus de code die we gaan testen: we zullen willekeurig een aantal wachtwoorden genereren met JSCheck en ervoor zorgen dat ze een juiste score krijgen.


Onze claim schrijven

Nu zijn we klaar om onze claims te schrijven. Ga eerst naar de JSCheck Github-pagina en download de jscheck.js het dossier. Ik vind het leuk om mijn tests in de terminal uit te voeren, via NodeJS, dus voeg deze enkele regel helemaal onderaan het bestand toe:

(typeof venster! == 'undefined'? window: exports) .JSC = JSC;

Dit heeft geen invloed op de manier waarop het bestand zich in de browser gedraagt, maar het zorgt ervoor dat het bestand als een module binnen het knooppunt werkt. Merk op dat de jscheck.js bestand bloot JSC als de enkele globale variabele voor de hele bibliotheek. Als we deze aanpassing niet doorgevoerd hebben, hebben we er toegang toe.

Laten we openen passwordScorerSpec.js en dingen beginnen:

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

Aangezien ik deze tests in NodeJS uitvoer, moeten we de modules die we nodig hebben hebben. Natuurlijk wilt u ervoor zorgen dat paden overeenkomen met uw bestandslocaties.

Nu zijn we klaar om onze eerste claim te schrijven. Natuurlijk gebruiken we de JSC.claim methode. Deze methode accepteert drie parameters, met een optionele vierde. De eerste parameter is slechts een tekenreeks, een naam voor de claim. De tweede parameter wordt de gezegde: het is de eigenlijke testfunctie. Heel eenvoudig, deze functie zou moeten terugkeren waar als de claim waar is, en vals als de claim onwaar is. De willekeurige waarden die JSCheck voor de test genereert, worden als parameters aan het predikaat doorgegeven.

Maar hoe weet JSCheck welk type willekeurige waarden het predicaat moet afgeven? Dat is waar de derde parameter, de specifier komt in het spel. Dit is een array, met een item voor elke parameter voor predikaat. De items in de array specificeren welke typen het predikaat moeten geven, met behulp van de specificatie-functies van JSCheck. Hier zijn er een paar:

  • JSC.boolean () retourneert waar of onwaar.
  • JSC.character () neemt een min- en max-teken en retourneert een enkel teken uit dat bereik. Het kan ook één karaktercode bevatten en dat karakter teruggeven.
  • JSC.integer () zal een priemgetal terugsturen. Of geef het een enkele parameter door om een ​​geheel getal (geheel getal) tussen 1 en de parameter te krijgen, of twee parameters voor een geheel getal in dat bereik.

Je snapt het idee. Er zijn nog andere bestekschrijvers en we zullen er nu wat gebruiken, terwijl we onze eerste claim schrijven.

JSC.claim ("All Smallcase Password" ;, function (wachtwoord, maxScore) retourneer PasswordScorer.score (wachtwoord) <= maxScore; , [ JSC.string(JSC.integer(10, 20), JSC.character('a', 'z')), JSC.literal(26) ]);

Onze eerste parameter is een naam. De tweede is de testfunctie: deze ontvangt een wachtwoord en een max-score en geeft true als de score voor dat wachtwoord kleiner is dan of gelijk is aan de max-score. Vervolgens hebben we onze specificier-array. Onze eerste parameter (het wachtwoord) zou een string moeten zijn, dus gebruiken we de JSC.string () methode: er kunnen twee parameters nodig zijn, het aantal tekens in de tekenreeks en de waarde voor die tekens. Zoals u ziet, vragen we om een ​​wachtwoord tussen 10 en 20 tekens. Voor de waarde gebruiken we de JSC.characters () methode om willekeurige tekens tussen 'a' en 'z' te krijgen.

De volgende waarde is onze maxScore parameter. Soms willen we de willekeurigheid van JSCheck niet, en dit is een van die tijden. Dat is waarom er is JSC.literal: om een ​​letterlijke waarde het predicaat door te geven. In dit geval gebruiken we 26, wat de maximumscore zou moeten zijn voor elk klein-in-hoofd-wachtwoord tussen 10 en 20 tekens.

Nu zijn we klaar om de test uit te voeren.


Onze claim uitvoeren

Voordat we de claim daadwerkelijk uitvoeren en het rapport ontvangen, moeten we de functie instellen die het rapport ontvangt. JSCheck geeft het rapport door aan een callback-functie van JSC.on_report. Vandaar:

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

Niets bijzonders. Nu is alles om te bellen JSC.check (). Nu kunnen we naar onze terminal gaan en dit uitvoeren:

knooppad / naar / wachtwoordScorerSpec.js

Achter de schermen draait JSCheck 100 keer het predicaat en genereert elke keer verschillende willekeurige waarden. U zou uw rapport moeten zien afgedrukt.

Alle kleine wachtwoorden 100 van 100 passen 100

Ze zijn allemaal geslaagd, maar dat is niet zo'n rapport, hè? Nou, als een van onze tests was mislukt, zouden ze zijn opgenomen in het rapport. U kunt het uitgangsniveau echter aanpassen met de JSC.detail functie: geef het een cijfer tussen 0 en 4 (inclusief) om alles voor geen output te krijgen voor alle testgevallen. De standaardwaarde is 3.


Een classificator toevoegen

Weet je nog hoe ik dat zei JSC.claim zou een vierde parameter kunnen nemen? Het heet a classifier, en het ontvangt dezelfde parameters die het predikaat ontvangt. Vervolgens kan het een string retourneren om onze testcases te classificeren of te groeperen. Ik geef toe dat ik niet echt zeker wist waar dit nuttig zou zijn totdat ik de bovenstaande voorbeeldclaim maakte. Kijk, ik maakte een fout in het predikaat en vergeleek de score met de maxScore met de operator in plaats van de operator, dus alle wachtwoorden die 26 punten scoorden, faalden. Ik zag rapporten die er ongeveer zo uitzagen:

Alle kleine wachtwoorden 96 van 100 FAIL [12] ("vqfqkqqbwkdjrvplkrx";, 26) FAIL [21] ("nhgkznldvoenhqqlfza";, 26) FAIL [62] ("eclloekuqhvnsyyuekj";, 26) FAIL [78] ("rvrkfivwtdphrhjrjis" ;, 26) geslaagd 96 mislukken 4

Het is nog steeds niet helemaal duidelijk waarom sommige tests falen. Dus ik heb een classificatiefunctie toegevoegd die de testcases op score heeft gegroepeerd: zoals ik zei, de functie heeft dezelfde parameters als het predikaat en retourneert een tekenreeks. Elke testcase die dezelfde string terug krijgt van de classifier, zal in het rapport worden gegroepeerd.

functie (wachtwoord, maxScore) retourneer PasswordScorer.score (wachtwoord) + "punten" ;; 

Deze functie zou de laatste parameter van onze claim moeten zijn. U krijgt nu een rapport dat ongeveer zo is:

Alle kleine wachtwoorden 96 van 100 FAIL [4] 26 punten :( "illqbtiubsmrhxdwjfo";, 26) FAIL [22] 26 punten :( "gruvmmqjzqlcyaozgfh";, 26) FAIL [34] 26 punten :( "chhbevwtjvslprqczjg";, 26 ) FAIL [65] 26 punten :( "kskqdjhtonybvfewdjm";, 26) 14 punten: pas 8 15 punten: pas 5 16 punten: pas 12 18 punten: pas 10 19 punten: pas 12 20 punten: pas 11 22 punten: pas 12 23 punten: pass 8 24 punten: pas 10 25 punten: pas 8 26 punten: pass 0 mislukken 4

U kunt zien hoe de tests gegroepeerd zijn op hoeveel punten de wachtwoorden waard zijn. Nu is het gemakkelijk om te zien dat de enige wachtwoorden die de tests niet halen, de wachtwoorden zijn die 26 punten scoren. En hoewel het probleem hier was met de test, en niet met de code, toont het nog steeds hoe het nuttig kan zijn om een ​​classificatiefunctie toe te voegen aan uw claims.


Laatste gedachten

Dus, aan het einde van de dag, is JSCheck het waard om te gebruiken? Dit is wat ik denk: het is niet iets dat je bij elke codebase zult gebruiken, maar soms vind je het nuttig om willekeurige testcases te maken die een bepaald stuk code rigoureus zullen testen. Wanneer dat is wat u wilt doen, heb ik daarvoor geen tool beter gezien dan JSCheck.

JSCheck heeft een paar andere opties en een aantal specificaties die we niet hebben besproken in deze zelfstudie; ga naar JSCheck.og om daarover te lezen. Anders zou ik graag uw mening horen over JSCheck in de comments!