Incontra il JSCheck di Crockford

Esistono dozzine di framework di test JavaScript, ma la maggior parte di essi funziona, più o meno, allo stesso modo. Tuttavia, il JSCheck di Douglas Crockford è notevolmente diverso dalla maggior parte. In questo tutorial ti mostrerò come è diverso e perché dovresti considerare di usarlo!


Crockford descrive JSCheck come uno "strumento di test guidato dalle specifiche.

Crockford descrive JSCheck come uno "strumento di test guidato dalle specifiche". Quando si usano i framework a cui si è abituati, si dovrebbe scrivere un test per una determinata funzionalità e, se il test passa, dichiarare che la funzionalità fornita funziona correttamente . Tuttavia, è possibile che tu possa perdere alcuni dei casi limite o eccezioni che i tuoi test non coprono.

Mentre scoprire casi limite non è lo scopo esplicito di JSCheck, è un vantaggio collaterale. L'idea principale alla base di JSCheck è questa: la specifica che scrivi descriverà in realtà come dovrebbe funzionare il codice che stai testando. Quindi, JSCheck prenderà quella specifica (chiamata a Richiesta in JSCheck-lingo) e genera test casuali per dimostrare il reclamo. Alla fine, ti riferirà i risultati.

Sembra interessante? Continuare a leggere! Suona familiare? Potresti aver utilizzato lo strumento di test Haskell, QuickCheck, su cui era basato JSCheck.


Qualche codice da testare

Naturalmente, prima di scrivere effettivamente il nostro reclamo, vorremmo avere un codice da testare. Recentemente, ho scritto uno scorer per mini-password, simile alla funzionalità su HowSecureIsMyPassword.net. Non è davvero elegante: basta passare la funzione a una password e ottenere un punteggio. Ecco il codice:

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]; per (var i = 1; i < re.length; i++)  letterScore += (password.match(re[i]) || []).length * i;  return letterScore + lengthScore; ; (typeof window !== 'undefined' ? window : exports).PasswordScorer = PasswordScorer; ());

È un codice piuttosto semplice, ma ecco cosa sta succedendo: il punteggio è composto da due sotto-punteggi. C'è un punteggio iniziale, che si basa sulla lunghezza della password, e quindi un punteggio aggiuntivo per ogni carattere, 1 punto per ogni lettera minuscola, 2 punti per ogni lettera maiuscola, 3 punti per ogni numero e 4 punti per ciascun simbolo ( da un set limitato).

Quindi, questo è il codice che testeremo: genereremo casualmente alcune password con JSCheck e ci assicureremo che ottengano un punteggio appropriato.


Scrivendo il nostro reclamo

Ora siamo pronti a scrivere le nostre affermazioni. Per prima cosa, vai sulla pagina Github di JSCheck e scarica il jscheck.js file. Mi piace eseguire i miei test nel terminale, tramite NodeJS, quindi aggiungi questa singola riga alla fine del file:

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

Ciò non influirà sul modo in cui il file si comporta nel browser, ma lo farà funzionare come un modulo all'interno del nodo. Si noti che il jscheck.js il file espone JSC come singola variabile globale per l'intera libreria. Se non stessimo effettuando questo aggiustamento, è così che accetteremmo.

Apriamo passwordScorerSpec.js e inizia le cose:

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

Poiché eseguo questi test in NodeJS, dovremo richiedere i moduli che vogliamo. Ovviamente, dovrai assicurarti che i percorsi corrispondano alle posizioni dei file.

Ora siamo pronti a scrivere la nostra prima richiesta. Certo, usiamo il JSC.claim metodo. Questo metodo accetta tre parametri, con un quarto opzionale. Il primo parametro è solo una stringa, a nome per il reclamo. Il secondo parametro è chiamato il predicato: è la funzione di test effettiva. Molto semplicemente, questa funzione dovrebbe tornare vero se il reclamo è vero, e falso se il reclamo è falso. I valori casuali che JSCheck genererà per il test verranno passati come parametri al predicato.

Ma come fa JSCheck a sapere quale tipo di valori casuali dare al predicato? Ecco dove il terzo parametro, il specificatore entra in gioco. Questo è un array, con un elemento per ogni parametro per il predicato. Gli elementi nell'array specificano i tipi da assegnare al predicato, usando le funzioni identificatore di JSCheck. Ecco alcuni di loro:

  • JSC.boolean () ritorna vero o falso.
  • JSC.character () prende un carattere min e max e restituisce un singolo carattere da quell'intervallo. Può anche prendere un codice di un singolo carattere e restituire quel personaggio.
  • JSC.integer () restituirà un numero primo. Oppure passare un singolo parametro per ottenere un numero intero (numero intero) compreso tra 1 e il parametro o due parametri per un numero intero in tale intervallo.

Hai un'idea. Ci sono altri specificatori, e ne useremo alcuni ora mentre scriviamo la nostra prima affermazione.

JSC.claim ("Tutte le password in minuscolo" ;, funzione (password, maxScore) return PasswordScorer.score (password) <= maxScore; , [ JSC.string(JSC.integer(10, 20), JSC.character('a', 'z')), JSC.literal(26) ]);

Il nostro primo parametro è un nome. La seconda è la funzione di test: riceve una password e un punteggio massimo e restituisce true se il punteggio per quella password è inferiore o uguale al punteggio massimo. Quindi, abbiamo il nostro array di specificatori. Il nostro primo parametro (la password) dovrebbe essere una stringa, quindi usiamo il JSC.string () metodo: può assumere due parametri, il numero di caratteri nella stringa e il valore per quei caratteri. Come puoi vedere, chiediamo una password tra 10 e 20 caratteri. Per il valore, stiamo usando il JSC.characters () metodo per ottenere caratteri casuali tra 'a' e 'z'.

Il prossimo valore è il nostro maxScore parametro. A volte, non vogliamo la casualità offerta da JSCheck, e questa è una di quelle volte. Ecco perché c'è JSC.literal: per passare un valore letterale al predicato. In questo caso, stiamo usando 26, che dovrebbe essere il punteggio massimo per qualsiasi password minuscola tra 10 e 20 caratteri.

Ora siamo pronti per eseguire il test.


Esecuzione della nostra richiesta

Prima di eseguire effettivamente il reclamo e ottenere il rapporto, dobbiamo impostare la funzione che riceverà il rapporto. JSCheck passa il report a una funzione di callback di JSC.on_report. Quindi:

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

Nulla di bello. Ora, tutto ciò che rimane è chiamare JSC.check (). Ora, possiamo andare al nostro terminale ed eseguire questo:

percorso nodo / su / passwordScorerSpec.js

Dietro le quinte, JSCheck esegue il predicato 100 volte, generando ogni volta valori casuali diversi. Dovresti vedere il tuo rapporto stampato.

Tutte le password in minuscolo 100 di 100 passano 100

Sono passati tutti, ma non è un granché, eh? Bene, se qualcuno dei nostri test avesse fallito, sarebbe stato incluso nel rapporto. Tuttavia, è possibile regolare il livello di output con JSC.detail funzione: passa un numero compreso tra 0 e 4 (incluso) per ottenere qualsiasi cosa per nessuna uscita in tutti i casi di test. Il valore predefinito è 3.


Aggiungere un classificatore

Ricorda come l'ho detto JSC.claim potrebbe prendere un quarto parametro? Si chiama a classificatore, e riceve gli stessi parametri ricevuti dal predicato. Quindi, può restituire una stringa per classificare, o raggruppare, i nostri casi di test. Devo ammettere che non ero sicuro di dove sarebbe stato utile fino a quando non ho creato la rivendicazione di esempio sopra. Vedi, ho fatto un errore nel predicato e ho confrontato il punteggio con il maxScore con il operatore invece del operatore, quindi tutte le password che hanno segnato 26 punti stavano fallendo. Stavo vedendo rapporti che assomigliavano a questo:

Tutte le password minuscole 96 di 100 FAIL [12] ("vqfqkqqbwkdjrvplkrx";, 26) FAIL [21] ("nhgkznldvoenhqqlfza";, 26) FAIL [62] ("eclloekuqhvnsyyuekj";, 26) FAIL [78] ("rvrkfivwtdphrhjrjis" ; 26) passare 96 fallire 4

Non è ancora del tutto ovvio perché alcuni test stiano fallendo. Così ho aggiunto una funzione di classificazione che raggruppava i casi di test in base al punteggio: come ho detto, la funzione assume gli stessi parametri del predicato e restituisce una stringa. Ogni test case che ottiene la stessa stringa dal classificatore verrà raggruppato nel report.

function (password, maxScore) return PasswordScorer.score (password) + "points" ;; 

Questa funzione dovrebbe essere l'ultimo parametro della nostra richiesta. Ora riceverai un rapporto simile a questo:

Tutte le password minuscole 96 di 100 FAIL [4] 26 punti :( "illqbtiubsmrhxdwjfo";, 26) FAIL [22] 26 punti :( "gruvmmqjzqlcyaozgfh";, 26) FAIL [34] 26 punti :( "chhbevwtjvslprqczjg";, 26 FAIL [65] 26 punti :( "kskqdjhtonybvfewdjm";, 26) 14 punti: passa 8 15 punti: passa 5 16 punti: passa 12 18 punti: passa 10 19 punti: passa 12 20 punti: passa 11 22 punti: passa 12 23 punti: passa 8 24 punti: passa 10 25 punti: passa 8 26 punti: passa 0 fallisce 4

Puoi vedere come i test sono raggruppati per quanti punti valgono le password. Ora, è facile vedere che le uniche password che non superano i test sono le password che totalizzano 26 punti. E mentre il problema qui era con il test, e non con il codice, mostra ancora come può essere utile aggiungere una funzione di classificazione alle tue richieste.


Pensieri finali

Quindi, alla fine della giornata, vale la pena utilizzare JSCheck? Ecco cosa penso: non è qualcosa che userai necessariamente con ogni base di codice, ma a volte ti sarà utile essere in grado di creare casi di test casuali che testeranno rigorosamente un determinato pezzo di codice. Quando è quello che vuoi fare, non ho visto uno strumento migliore di quello di JSCheck.

JSCheck ha alcune altre opzioni e un sacco di specificatori che non abbiamo esaminato in questo tutorial; andare su JSCheck.og per leggere di questi. Altrimenti, mi piacerebbe sentire i tuoi pensieri su JSCheck nei commenti!