TJ Holowaychuk

Month

March 2012

4 posts

Mocha 1.0

The Mocha JavaScript test framework has hit 1.0 with a bunch of great contributions from the community, here’s the change log:

  • Added js API. Closes #265
  • Added: initial run of tests with --watch. Closes #345
  • Added: mark location as a global on the CS. Closes #311
  • Added markdown reporter (github flavour)
  • Added: scrolling menu to coverage.html. Closes #335
  • Added source line to html report for Safari [Tyson Tate]
  • Added “min” reporter, useful for --watch [Jakub Nešetřil]
  • Added support for arbitrary compilers via . Closes #338 [Ian Young]
  • Added Teamcity export to lib/reporters/index [Michael Riley]
  • Fixed chopping of first char in error reporting. Closes #334 [reported by topfunky]
  • Fixed terrible FF / Opera stack traces
Compiler support

coffee-script out of the box was removed, now you can used the --compilers <ext>:<module>,... flag to map a compiler to the given extension name. For example mocha --compilers coffee:coffee-script. There are simply too many foo -> JavaScript transpilers to directly support, this pushes that back on the author.

Min reporter

First up is the min reporter by Jakub Nešetřil, this tiny reporter works great with --watch, outputting the summary only, though still reporting verbose errors on failure.

Markdown reporter

I added a markdown reporter which can be used to display your tests as documentation in a Github wiki page, or simply a markdown file in your repository that you can link to. For example here are the Connect markdown test docs.

I’m not super happy with how much padding Github adds, so the TOC looks pretty messy, but all of these document-style reporting mechanisms make you think twice about how clean and organized your tests are.

JavaScript API

A new JS API was added, which mocha(1) now utilizes. This higher-level JS API will make it easier for those who want to script the testing process with Mocha. I have yet to document this API but it looks like this:

var Mocha = require('mocha');

var mocha = new Mocha;
mocha.reporter('spec').ui('bdd');

mocha.addFile('test/suite.js');
mocha.addFile('test/runner.js');
mocha.addFile('test/runnable.js');

var runner = mocha.run(function(){
  console.log('finished');
});

runner.on('pass', function(test){
  console.log('... %s passed', test.title);
});

runner.on('fail', function(test){
  console.log('... %s failed', test.title);
});
Mar 24, 201214 notes
#mocha #nodejs #javascript
Mar 24, 201240 notes
Redis Lua scripting is badass

Roughly a year ago Salvatore Sanfilippo the author of Redis wrote a blog post discussing the inclusion of Lua as a scripting language. I finally decided to try this out, and let’s just say it’s pretty badass.

Lua is a great fit for Redis, they have similar philosophies, being simple, small, and fast. Suppose for example you have 200,000 jobs, each represented in Redis as a hash, and you want to map/reduce the job duration, the new scripting capabilities make this really easy!

Here’s the node setup script to generate these jobs:

  var redis = require('redis')
    , db = redis.createClient();

  var n = 500000
    , pending = n
    , ms;

  while (n--) {
    ms = Math.random() * 200 | 0;
    db.hset('job:' + n, 'duration', ms, function(){
      --pending || process.exit();
    })
  }

Next here is what you might consider scripting in your host language without the new Redis scripting feature, manually reducing the value:

  var redis = require('redis')
    , db = redis.createClient();

  var n = 200000
    , start = new Date
    , pending = n
    , ms = 0;

  while (n--) {
    db.hget('job:' + n, 'duration', function(err, n){
      if (err) throw err;
      ms += ~~n;
      --pending || (function(){
        console.log('%d minutes spent processing jobs', ms / (1000 * 60) | 0);
        console.log('took %ds', (new Date - start) / 1000 | 0);
        process.exit();
      })();
    })
  }

On my Air this took roughly 7s, not too great, keep in mind that there is no throttling here I’m just plastering it with 200k commands. Now let’s try it with Lua! The following script is ad-hoc, but it’ll do the trick. To signal an error all you have to do is return a table with the err slot. redis.call() is effectively the public Redis API exposed to your Redis script, so you can use it just like you would your host language Redis bindings or redis-cli(1).

   local sum = 0
   for i = 0, 200000, 1 do
     local key = "job:" .. i
     local ms = tonumber(redis.call("hget", key, "duration"))
     if ms == nil then return { err = key .. " is not an integer" } end
     sum = sum + ms
   end
   return sum

Here I’ve embedded it in the JS script, but you could of course generate these, load them from files etc (beware of redis-injection?).

   var redis = require('redis')
     , db = redis.createClient();

   var script = '\
   local sum = 0 \
   for i = 0, 200000, 1 do \
     local key = "job:" .. i \
     local ms = tonumber(redis.call("hget", key, "duration")) \
     if ms == nil then return { err = key .. " is not an integer" } end \
     sum = sum + ms \
   end \
   return sum';

   var start = new Date;

   db.eval(script, 0, function(err, ms){
     if (err) throw err;
     console.log('%d minutes spent processing jobs', ms / (1000 * 60) | 0);
     console.log('took %dms', new Date - start | 0);
     process.exit();
   });

After running the script with the new EVAL command what previously took several seconds dropped to ~850ms, much better. EVAL and EVALSHA are actually a lot more flexible than I’ve explained here, accepting keys and arbitrary arguments.

Check out antirez.com/post/an-update-on-redis-and-lua.html for more.

Mar 14, 201216 notes
#redis #lua #node #js
Mocha string diffs

Mocha 0.14.0 adds a single feature - string diffs! This is a small but very handy feature for some. When the strings are small, Mocha will use a character diff, when consisting of several lines a line-numbered “gutter” is added and a word diff is used as shown in the following image:

This is very useful when authoring things like template engines, transpilers, and other string-based libraries. For example the Stylus test suite is comprised of nothing but acceptance tests, the input file is compiled, and the resulting CSS is checked using actual.trim().should.equal(css);. In combination with Mocha’s BDD interface I can now simply iterate through the files and define test-cases as shown here:

  var stylus = require('../')
    , fs = require('fs');

  // test cases

  var cases = fs.readdirSync('test/cases').filter(function(file){
    return ~file.indexOf('.styl');
  }).map(function(file){
    return file.replace('.styl', '');
  });

  describe('integration', function(){
    cases.forEach(function(test){
      var name = test.replace(/[-.]/g, ' ');
      it(name, function(){
        var path = 'test/cases/' + test + '.styl';
        var styl = fs.readFileSync(path, 'utf8');
        var css = fs.readFileSync('test/cases/' + test + '.css', 'utf8');

        var style = stylus(styl)
          .set('filename', path)
          .include(__dirname + '/images')
          .include(__dirname + '/cases/import.basic')
          .define('url', stylus.url());

        if (~test.indexOf('compress')) style.set('compress', true);

        style.render(function(err, actual){
          if (err) throw err;
          actual.trim().should.equal(css);
        });
      })
    });
  })

Now if something were go to wrong, you get a nice diff!

Assertion library authors

To support this feature all you have to do is populate err.expected and err.actual with their respective values, Mocha will take care of the presentation.

NOTE: I just toggled the colors, green is now the expected color

Mar 1, 20125 notes
#mocha #js #node
Next page →
2012 2013
  • January
  • February 3
  • March
  • April
  • May
  • June
  • July
  • August
  • September
  • October
  • November
  • December
2011 2012 2013
  • January 2
  • February 2
  • March 4
  • April 3
  • May 2
  • June 2
  • July 4
  • August 3
  • September 1
  • October 2
  • November 1
  • December 5
2010 2011 2012
  • January
  • February 3
  • March
  • April 4
  • May 2
  • June 1
  • July 3
  • August 3
  • September 3
  • October
  • November 3
  • December 1
2010 2011
  • January
  • February
  • March
  • April 8
  • May 1
  • June 3
  • July 4
  • August 4
  • September 3
  • October
  • November 1
  • December 1