Advertisement
JavaScript & AJAX

Meet Crockford’s JSCheck

by

There are dozens of JavaScript testing frameworks, but most of them function in, more or less, the same way. However, Douglas Crockford’s JSCheck is considerably different from most. In this tutorial, I’ll show you how it’s different and why you should consider using it!


Crockford describes JSCheck as a “specification-driven testing tool.

Crockford describes JSCheck as a “specification-driven testing tool.” When using the frameworks you’re used to, you would write a test for a given piece of functionality, and, if that test passes, declare that the given functionality is working correctly. However, it’s possible that you might miss some of edge cases or exceptions that your tests don’t cover.

While uncovering edge cases isn’t the express purpose of JSCheck, it is a nice side benefit. The main idea behind JSCheck is this: the specification you write will actually describe how the code you are testing should work. Then, JSCheck will take that specification (called a claim in JSCheck-lingo), and generate random tests to prove the claim. Finally, it will report the results to you.

Sounds interesting? Read on! Sounds familiar? You might have used the Haskell testing tool, QuickCheck, on which JSCheck was based.


Some Code to Test

Of course, before actually writing our claim, we’ll want to have some code to test. Recently, I wrote a mini-password scorer, similar to the functionality on HowSecureIsMyPassword.net. It really isn’t fancy: you just pass the function a password and get a score back. Here’s the 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, /[a-z]/g, /[A-Z]/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;
}());

It’s pretty simple code, but here’s what’s going on: the score is made up of two sub-scores. There’s a starting score, that’s based on the length of the password, and then an additional score for each character, 1 point for each lowercase letter, 2 points for each uppercase letter, 3 points for each number, and 4 points for each symbol (from a limited set).

So, this is the code we’re going to test: we’ll randomly generate some passwords with JSCheck and make sure they get an appropriate score.


Writing our Claim

Now we’re ready to write our claims. First, head over the JSCheck Github page and download the jscheck.js file. I like to run my tests in the terminal, via NodeJS, so add this single line to the very bottom of the file:

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

This won’t affect the way the file behaves in the browser at all, but it will make it work as a module within Node. Notice that the jscheck.js file exposes JSC as the single global variable for the whole library. If we weren’t making this adjustment, that’s how we’d access it.

Let’s open passwordScorerSpec.js and start things:

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

Since I’m running these tests in NodeJS, we’ll have to require the modules we want. Of course, you’ll want to make sure that paths match your file locations.

Now, we’re ready to write our first claim. Of course, we use the JSC.claim method. This method accepts three parameters, with an optional fourth. The first parameter is just a string, a name for the claim. The second parameter is called the predicate: it’s the actual testing function. Very simply, this function should return true if the claim is true, and false if the claim is false. The random values that JSCheck will generate for the test will be passed as parameters to the predicate.

But how does JSCheck know what type of random values to hand the predicate? That’s where the third parameter, the specifier comes into play. This is an array, with an item for each parameter for predicate. The items in the array specify what types to give the predicate, using JSCheck’s specifier functions. Here are a few of them:

  • JSC.boolean() returns either true or false.
  • JSC.character() takes a min and max character and returns a single character from that range. It can also take a single character code and return that character.
  • JSC.integer() will return a prime number. Or, pass it a single parameter to get an integer (whole number) between 1 and the parameter, or two parameters for an integer in that range.

You get the idea. There are other specifiers, and we’ll use some now as we write our first claim.

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

Our first parameter is a name. The second is the testing function: it receives a password and a max score, and returns true if the score for that password is less than or equal to the max score. Then, we have our specifier array. Our first parameter (the password) should be a string, so we use the JSC.string() method: it can take two parameters, the number of characters in the string, and value for those characters. As you can see, we’re asking for a password between 10 and 20 characters. For the value, we’re using the JSC.characters() method to get random characters between ‘a’ and ‘z’.

The next value is our maxScore parameter. Sometimes, we don’t want the randomness that JSCheck offers, and this is one of those times. That’s why there’s JSC.literal: to pass a literal value the predicate. In this case, we’re using 26, which should be the max score for any all-lowercase password between 10 and 20 characters.

Now we’re ready to run the test.


Running our Claim

Before we actually run the claim and get the report, we have to setup the function that will receive the report. JSCheck passes the report to a callback function of JSC.on_report. Hence:

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

Nothing fancy. Now, all that’s left is to call JSC.check(). Now, we can head to our terminal and run this:

node path/to/passwordScorerSpec.js

Behind the scenes, JSCheck runs the predicate 100 times, generating different random values each time. You should see your report printed out.

All Lowercase Passwords 100 of 100
 pass 100

They all passed, but that’s not much of a report, eh? Well, if any of our tests had failed, they would have been included in the report. However, you can adjust the level of output with the JSC.detail function: pass it a number between 0 and 4 (inclusive) to get anything for no output to all the test cases. The default value is 3.


Adding a Classifier

Remember how I said that JSC.claim could take a fourth parameter? It’s called a classifier, and it receives the same parameters that the predicate receives. Then, it can returns a string to classify, or group, our test cases. I’ll admit I wasn’t really sure where this would be useful until I was creating the above example claim. See, I made a mistake in the predicate and compared the score to the maxScore with the < operator instead of the <= operator, so any passwords that scored 26 points were failing. I was seeing reports that looked something like this:

All Lowercase Passwords 96 of 100
 FAIL [12] ("vqfqkqqbwkdjrvplkrx";,26)
 FAIL [21] ("nhgkznldvoenhqqlfza";,26)
 FAIL [62] ("eclloekuqhvnsyyuekj";,26)
 FAIL [78] ("rvrkfivwtdphrhjrjis";,26)
 pass 96 fail 4

It’s still not entirely obvious why some tests are failing. So I added a classifier function that grouped the test cases by score: like I said, the function takes the same parameters as the predicate, and it returns a string. Every test case that gets the same string back from the classifier will be grouped together in the report.

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

This function should be the last parameter of our claim. Now, you’ll get a report that’s something like this:

All Lowercase Passwords 96 of 100
 FAIL [4] 26 points:("illqbtiubsmrhxdwjfo";,26)
 FAIL [22] 26 points:("gruvmmqjzqlcyaozgfh";,26)
 FAIL [34] 26 points:("chhbevwtjvslprqczjg";,26)
 FAIL [65] 26 points:("kskqdjhtonybvfewdjm";,26)
14 points: pass 8
15 points: pass 5
16 points: pass 12
18 points: pass 10
19 points: pass 12
20 points: pass 11
22 points: pass 12
23 points: pass 8
24 points: pass 10
25 points: pass 8
26 points: pass 0 fail 4

You can see how the tests are grouped by how many points the passwords are worth. Now, it’s easy to see that the only passwords that fail the tests are the passwords that score 26 points. And while the problem here was with the test, and not the code, it still shows how it can be useful to add a classifier function to your claims.


Final Thoughts

So, at the end of the day, it JSCheck worth using? Here’s what I think: it’s not something you’re necessarily going to use with every code base, but sometimes you’ll find it useful to be able to create random test cases that will rigorously test a given piece of code. When that’s what you want to do, I haven’t seen a tool better for that than JSCheck.

JSCheck has a few other options and a bunch of specifiers that we haven't reviewed in this tutorial; head over to JSCheck.og to read about those. Otherwise, I’d love to hear your thoughts about JSCheck in the comments!

Related Posts
  • Computer Skills
    Hardware
    How to Check and Enable TRIM on a Mac SSDTrim preview retina
    You probably know that solid state drives (SSD) differ from Hard Disk Drives (HDD) in how they store information, and you may have heard that something called TRIM can maintain their performance. In this tutorial, I’ll not only show you how you can enable TRIM support for your SSDs, but also understand what the term means and how it fits into the functionality of solid state storage.Read More…
  • Computer Skills
    OS X
    Finding Hardware Faults: Exploring AHT & Apple DiagnosticsAht preview retina
    Alongside the tools provided for identifying software issues with your Mac, Apple also makes sure that you can examine your hardware for possible faults. These hardware diagnostic tools have evolved over the years, so in this tutorial I will cover the two incarnations that exist: the venerable Apple Hardware Test (AHT), and the newer Apple Diagnostics tool that replaced it. In this tutorial I'll show you how you can use these helpful utilities to keep an eye on the components that make your Mac tick.Read More…
  • Code
    PHP
    Validation and Exception Handling: From the UI to the BackendProcedural to oop php retina preview
    Sooner or later in your programming career you will be faced with the dilemma of validation and exception handling. This was the case with me and my team also. A couple or so years ago we reached a point when we had to take architectural actions to accommodate all the exceptional cases our quite large software project needed to handle. Below is a list of practices we came to value and apply when it comes to validation and exception handling.Read More…
  • Code
    JavaScript & AJAX
    Testing in Node.jsNodejs testing chai retina preview
    A test driven development cycle simplifies the thought process of writing code, makes it easier, and quicker in the long run. But just writing tests is not enough by itself, knowing the kinds of tests to write and how to structure code to conform to this pattern is what it's all about. In this article we will take a look at building a small app in Node.js following a TDD pattern.Read More…
  • Code
    JavaScript & AJAX
    Better CoffeeScript Testing With MochaMocha coffeescript
    Recently, I’ve been doing a considerable amount of CoffeeScript work. One problem I ran into early-on was testing: I didn’t want to manually convert my CoffeeScript to JavaScript before I could test it. Instead, I wanted to test from CoffeeScript directly. How’d I end up doing it? Read on to find out!Read More…
  • Code
    JavaScript & AJAX
    Build Your First JavaScript LibraryYour first js library
    Ever marvelled at the magic of Mootools? Ever wondered how Dojo does it? Ever been curious about jQuery's gymnastics? In this tutorial, we’re going to sneak behind the scenes and try our hand at building a super-simple version of your favorite library.Read More…