Erik's blog

Code, notes, recipes, general musings

Archive for December 2010

Running YETI tests automatically with Watchr

with 2 comments

YETI (YUI Easy Testing Interface) provides an easy, automated way to run YUI 3 tests. Watchr provides an easy way to run arbitrary Ruby based on file system events. Putting the two together, we get an easy way to run YETI when a YUI 3 script is saved.

Prerequisites:

  • Mac 10.6.4.  What follows may work elsewhere, but I haven’t tried it yet

Set up:

  1. Install Watchr. Please refer to the readme file in Watchr’s github repository for installation instructions. I wrote a post the other day about getting started with Watcher on Mac 10.6.4.
  2. Install Node.js.  YETI requires Node.js.  Please refer to the Node.js documentation for downloading and building Node
  3. Install npm.  YETI can be installed easily with npm.  Please refer to the readme file in npm’s github repository for installation instructions.  The Joyent blog also has an informative post on Installing Node and npm.
  4. Install YETI: npm install yeti
  5. Create the following directories: test and lib.  These directory names are completely arbitrary, but they match the watchr script introduced below.  If you want to use different names, please update the watchr script accordingly
  6. Create a file called autotest.watchr and put the following ruby into it: https://gist.github.com/738737#file_autotest.watchr
  7. Create a file called test_example.html in the test directory and put the following html in it: https://gist.github.com/738737#file_test_example.html
  8. Create one last file called example.js in the lib directory and put the following javascript in it: https://gist.github.com/738737#file_example.js

You should now have a file structure like this:
/autotest.watchr
/lib/example.js
/test/test_example.html

Let’s run this rig:

  1. In your terminal, launch Watchr: watchr autotest.watchr
  2. Edit /lib/example.js so Y.example is no longer set to “foo”, e.g., Y.example = “bar”;
  3. Save /lib/example.js and view your terminal.  You should see YETI’s output of the failing test results
    Screen shot of YUI test failure
  4. Edit /lib/example.js resetting Y.example to “foo”, save, and note YETI’s output of the successful test results
    Screen shot of YETI output showing YUI tests passing
  5. Kill watchr (when you’re ready): Ctrl+C

Going forward:

Using the autotest.watchr script above, any file named /test/test_{lib name}.html will be run when /lib/{lib name}.js is edited.  The test file will also be run when it is edited.  If you add a new lib, but do not define a corresponding test file, watchr will fail silently.  Likewise, if you add a test file, but don’t put YUI tests in it.  In short, add libs and YUI tests in pairs, and you’re all good.

In closing, here’s one of my favorite songs from Drive Like Jehu:

Written by Erik

December 13, 2010 at 12:49 am

Posted in tutorial

Tagged with , , , , ,

Getting started with Watchr (and trying again to install Node.js on Mac 10.6.4)

with one comment

I recently started exploring testing options for Node.js. Yesterday, I wrote about my experiences with nodeunit. Today, I found Christian Johansen’s blog post Unit testing node.js apps. (Thanks for the write-up, Christian!) Although I was looking for unit testing options, what really got me excited was his mention of Watchr.

Watchr provides a way to run tests automatically in response to system events, e.g., when a file is saved, much like Autotest. I had fallen in love with Autotest’s functionality after learning about it in Micheal Hartl’s nice Ruby on Rails tutorial. According to Watchr’s docs, Autotest leaves something to be desired, but in any case I very much would like my tests to run without my having to think about it.

Git-ting (ha!) Watchr was easy enough, but to run Node tests on my Mac, which for some reason is an idea I’m hung up on, I need Node, and to date I haven’t been able to build Node on my Mac (10.6.4) successfully, so this is my challenge. After searching here and there, I found an archived thread from the Node mailing list that seemed promising. It mentions that MacPorts can break if I upgrade to Snow Leopard without upgrading MacPorts, which I had, and that this can prevent Node from compiling. After clicking through to the MacPorts migration docs, I followed the steps outlined there and I was able to build Node like this:

  1. I had tried and failed to build Node multiple times, so I blew away the build directory: rm -rf build
  2. ./configure
  3. Clean things up to be thorough: make clean
  4. make
  5. Run tests just in case: make test
  6. sudo make install

Ok, on to the testing. Here’s my folder structure:

project/
    – autotest.watchr
    – lib/
      – example.js
    – test/
       – test_example.js

My autotest.watchr file is a blend of the one on Christian’s blog, and Watchr’s tests.watchr prepackaged script. It contains

watch( 'test/test_.*\.js' )  {|md| system("node #{md[0]}") }
watch( 'lib/(.*)\.js' )      {|md| system("node test/test_#{md[1]}.js") }

# --------------------------------------------------
# Signal Handling
# --------------------------------------------------
# Ctrl-\
Signal.trap('QUIT') do
  puts " --- Running all tests ---\n\n"
  run_all_tests
end

# Ctrl-C
Signal.trap('INT') { abort("\n") }

example.js contains

exports.foo = 'bar';

test_example.js contains

var assert = require("assert");
var example = require('../lib/example');

assert.strictEqual(example.foo, 'bar', 'var foo should be "bar"');

I fire up watchr like this: watchr autotest.watchr

Watchr then captures the terminal until I enter Ctrl+C. Saving either example.js or test_example.js causes test_example.js to run. At this point the tests are crude, so my output is nothing if the test passes, or an assertion error, e.g., “AssertionError: var foo should be “bar””, if the test fails.

I think this is a good start. Time to listen to some Bonobo and call it a day.

Written by Erik

December 9, 2010 at 1:17 am

Getting started with unit testing for Node.js

with 3 comments

I’m diving into unit testing with Node.js, and my first stop is nodeunit. Luckily, Caolan McMahon wrote an excellent introduction to nodeunit on his blog. Thanks, Caolan.

I installed nodeunit via npm no problem: npm install nodeunit

All the examples in the Installing nodeunit section worked fine, but I needed to add
var events = require('events');
to the first code sample in the Testing asynchronous code section to get those tests to pass. So, the top of my test-doubled.js file looks like:

var doubled = require('../lib/doubled');
var events = require('events');
...

Farther down in the blog post, in the Shared state and sequential testing section, there’s a code sample with the events include in it, so I think I’m on the right track.

In the Test cases, setUp and tearDown section, I had trouble getting the tests to run. After referencing the project’s README file, I tried adding a callback arg to setUp() and tearDown(), and calling the callback, which worked. So, my code looks like:

...

var testCase = require('nodeunit').testCase;

exports.read = testCase({
setUp: function (callback) {
this._openStdin = process.openStdin;
this._log = console.log;
this._calculate = doubled.calculate;
this._exit = process.exit;

var ev = this.ev = new events.EventEmitter();
process.openStdin = function () { return ev; };

callback();
},
tearDown: function (callback) {
// reset all the overidden functions:
process.openStdin = this._openStdin;
process.exit = this._exit;
doubled.calculate = this._calculate;
console.log = this._log;

callback();
},
...

With the minor tweaks above, I was able to get all tests to pass:
Screenshot of all tests passing

🙂

Written by Erik

December 6, 2010 at 11:11 pm

Posted in notes

Tagged with , ,

Notes from Ryan Dahl’s talk “On Node.js” at Cloudstock

leave a comment »

Here are my notes from Ryan Dahl’s talk “On Node.js” at Salesforce’s Cloudstock event on 12/6:

  • Google put a lot of thought into v8 performance
  • Node is a set of bindings to v8 to allow js to do non-browser things
  • “I/O needs to be done differently”
  • There’s a big difference between dealing w/ stuff from the cache vs from a network.
  • nginx is just 3x better than apache in terms of # concurrent clients x # req/sec, but nginx’s mem usage is nearly constant vs apache’s steep curve towards 40 mb
  • apache uses a thread for each request, whereas nginx uses a single thread w/ an event loop
  • it’s well known that you need to use an event loop f you want to go crazy w/ concurrency
  • but even w/ an event loop, you pay dearly for blocking processes
  • we should be writing all of our i/o using non-blocking calls
  • sleep() is a blocking operation
  • Why “transfer encoding: chunked” by default? because we don’t know the full size of the response, and we can start returning immediately
  • each connection costs 1-2 kb minimum
  • random, humorous paraphrase: … !==, not !=, i hate javascript. Coding on stage is so difficult …
  • questions
    • when is node going to become stable? we have a stable branch (0.2), and 0.3 branch will break backwards compatability. I want this to be awk. I want it to be a unix util
    • how’s the hosting landscape looking? joyent is working on a special service for node, which is in its beta. heroku has some stuff. you can always use a general vps w/ an ops layer like monit.
    • interesting applications? node is good for realtime, massive concurrency sorts of things. wargemez.mape.me is a bot on an irc room which geolocates chats btwn people in a room.
    • what do you think about express? express is a web framework for node. it looks cool
    • what do you think about node being tied to v8? untying it doesn’t make sense at this point, but I’m appy w/ v8.

Written by Erik

December 6, 2010 at 12:32 pm

Posted in notes

Tagged with , , , ,