How to test your node.js app

May 2010

I’ve wanted to hack on a node.js project for a while, and a new app idea has given me the perfect excuse. My first question was: how do I test this? It’s a fairly new field out there, and there isn’t much help from node.js itself: it’s much more like Rack than a proper framework. So I spent some time coming up with one way to do it.

Disclaimer: I’m not that experienced with Javascript, particularly with the best way to define objects. I’d be grateful for patches to help improve the quality of the code here. I’ve also borrowed heavily from apprentice-us - thanks to Dave and Corey!

Overview

This is what I’ve got so far (the actual app I’m working will remain closed-source for the mo):

Example node.js github project

You probably want to refer to the code whilst reading the rest of this article.

To run the tests, run rake. To start the app, run node app.js (you will need to have node.js installed obviously).

If you install the watchr gem, and run watchr autotest.watchr, you’ll get robust autotest like functionality. I’m liking watchr much better than ZenTest right now.

How it works

The basic premise is to decouple the request/response handler from the server (see app.js, lib/http.js and lib/router.js). The interesting bit is in test/ integration/ user_sees_homepage.js - this then calls the dispatch method directly, passing in dummy Request and Response objects.

Note how I’ve defined the Response object. This allows me currently to write an integration test that looks like this:

router.dispatch(new Request("GET", "/"), new Response(function(headers, data) {
  assert.contains("200", headers['status'])
  assert.contains("Hello, world!", data)
}));

The assert.contains() method is not part of node.js: it’s implemented in test/helper.js.

The reason you need the asserts to be fired in the end() function is that node.js is inherently asynchronous and will finish executing this file whilst waiting for the haml file to load in lib/router.js. Try it yourself: if you put an assert at the bottom of the file it will fire immediately.

Unit tests

The plan is then to define whichever unit tests you need in test/ unit/ (something)_test.js, with the corresponding code in lib/ models/ (something).js. Just run javascript code in here and call methods on assert, and rake will execute it.

Improvements

You could potentially use the Sinatra-like framework Express to define lib/router.js - I’ve handrolled it for the moment. I’m of the opinion that you spot betterrefactorings by handrolling to start with: it could be that express.js isn’t right for my app, and I can’t easily tell yet.

There are a number of javascript testing libraries out there, but at the moment I’m happy with my own handrolled version, which just relies on the ‘assert’ package that node.js provides. There’s nothing to stop you using JSpec or some other javascript testing library: I wanted to keep things simple to start with.

I’m also aware that Cucumber now supports javascript through V8, which is an important step in the right direction. Unfortunately however it doesn’t yet support the commonjs package system, and doesn’t run through node.js but through raw V8. This makes it hard to use with anything but toy examples. Ideally I’ve love to plug Cucumber in in the future, if we can get it to use node.js as the platform somehow.

If you use it for something useful, let me know! I’d be very happy to receive patches and suggestions.

Share


More articles

A cache-busting http server script in ruby

"All of this can be yours/just give me what I want/and no one gets hurt"

– Bono, Vertigo

If you’ve done much Javascript development, or simple web development without a webserver backend, you don’t want to set up a complex framework. Just give me the pages: I want to be able to start a simple webserver to give me the current directory structure as a website. You can’t simply load the pages into a browser using file:// because that screws up the relative paths that our sites rely on. What’s the best way of doing this?

Python’s SimpleHTTPServer

One simple way is:

python -m SimpleHTTPServer

This does a great job, but there’s one small problem: caching. Ordinarily during development you’ll want the browser to request the HTML each time, and the python server doesn’t do that out of the box.

Ruby’s WEBrick with adding cache-busting

Here’s a small script I borrowed from pmarti and tweaked. It lives in the bin/http file on my path: I just type http in the relevant folder and I’m set.

#!/usr/bin/env ruby

require 'webrick'
class NonCachingFileHandler < WEBrick::HTTPServlet::FileHandler
  def prevent_caching(res)
    res['ETag']          = nil
    res['Last-Modified'] = Time.now + 100**4
    res['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
    res['Pragma']        = 'no-cache'
    res['Expires']       = Time.now - 100**4
  end

  def do_GET(req, res)
    super
    prevent_caching(res)
  end
end

server = WEBrick::HTTPServer.new :Port => 8989

server.mount '/', NonCachingFileHandler , Dir.pwd
trap('INT') { server.stop }
server.start

Hope it’s helpful. Do you know of a better way of doing it? Feel free to share…

Read more

How to Build a Robust LLM Application

Meal Generator

Last month at Cherrypick we launched a brand new meal generator that uses LLMs to create personalized meal plans.

It has been a great success and we are pleased with the results. Customers are changing their plans 30% less and using their plans in their baskets 14% more.

However, getting to this point was not straightforward, and we learned many things that can go wrong when building these types of systems.

Here is what we learned about building an LLM-based product that actually works, and ends up in production rather than languishing in an investor deck as a cool tech demo.

Read more

Your Code Is A Liability

Every chunk of code you commit is more for someone else to read, digest and understand.

Every complex “clever” expression requires another few minutes of effort for each of your team. They must now interpret what you wrote and why you wrote it.

Every line you add limits your project’s responsiveness to change.

Your code is a liability. Never forget this.

Read more

The Sol Trader Christmas Eve update: moddable missions

The relative radio silence from Sol Trader Towers is for a reason: I’ve been working hard on a flexible and moddable mission structure, that allows players to take a variety of interesting quests in-game.

This build is now available on the forums should you have access (there’s still time if you don’t.)

kill mission

I’ve built a few missions to start with, including delivering parcels for business or personal reasons, taking characters on business trips and making other characters disappear. It’s great fun to have a variety of things to do for characters now and adds yet more colour to the game. Because it’s completely moddable, I’m also excited to see what storylines other people come up with!

Under the hood

The full details of how to create your own missions are available as a lengthy forum post, which will be kept up to date with changes and clarifications. Here’s an overview:

The missions are organised into packs, which exists under the data/missions subfolder. If you have access to the beta builds, you’ll see there’s one pack there already: these are the missions that are built in to the game.

There are several csv files in each mission folder:

  • requirements.csv: This file details the cases in which this mission might be triggered. Each character in the game has a chance of picking this mission (and becoming the ‘giver’ of the mission), based on the conditions imposed by this file.
  • conversation_player.csv: The extra conversation options available to the player because of this mission.
  • conversation_ai_response.csv: The extra options the AI can choose from as conversation responses.
  • opinions.csv: The extra opinion triggers, used for reactions to the generation and completion of these missions.
  • strings.csv: The new strings needed for the previous CSV files.

The possibilities for you to build your own missions are expanding all the time, as I add new missions triggers and possible goals for the AI.

business trip

What’s next?

At the moment it’s possible to take on any mission from any person, which isn’t very realistic. I need to allow players to gain other character’s trust, so that they will only give you sensitive missions in certain cases. Additionally it will soon be possible to start a career with an organisation, which will give you a rank, a certain amount of built in trust, and access to more senior characters.

I’m also going to be working on the in-space AI very soon. At the moment only freelance traders fly around between planets: it’s time we had passenger ships, military guards and pirates thrown into the mix.

Have a fantastic Christmas and I’ll see you all in the new year with some more updates.

Read more

New Sol Trader beta: the science of blame and unforgiveness

Previously I wrote about how I’m modelling opinions and prejudice in Sol Trader. It’s time to put some of that information to use.

The opinions a character has of other people, based on the partial events that they know about them, will now directly affect the things that happen in the history generation. This creates new events, which will in turn feed more character opinions.

There’s a new beta available on the forums if you have insider access.

Dudley and Meredith

In the example on the left, we can see that an acrimonious divorce of Meredith’s parents has left an indelible mark on her childhood. She now has a very low opinion of her father, Dudley.

When characters are adults, they can then generate a series of ‘favours’ (or ‘missions’) that they want completed. This is a source of work for the players, although completing certain missions does have real consequences on your relationships with the target of the mission. If they find out you’ve taken a mission against them, then they won’t be happy with you.

To continue our example, Meredith, whom we are now married to, wants us to find out some potentially incriminating information about our own father-in-law, Dudley. It’s up to us whether we take it or not. If he finds out, we’ll make an enemy of him.

Is it worth getting involved in this feud?

As the game goes on, the player will get embroiled in these relationships between the various characters and be able to directly affect their stories. Choosing what to take on and who to ally yourself with forms a major part of Sol Trader’s gameplay.

Sarina’s spiral of doom

Another example: the sad tale of Sarina, our older half sister. I picked Dagny and Warren in history generation to be my character’s parents, knowing that Dagny was cheating on her husband Hayden, mostly to see what happened. Little did I know how much it would affect Sarina, Dagny and Hayden’s eight year old daughter. When she found out about my birth, she got very upset.

She didn’t blame me, thankfully, although she never thought much of me. However, she never really spoke to our mother again, especially since her beloved father Hayden died soon after we were born.

She left home at a young age, and became a political assistant, but she didn’t make too many friends. She was doing ok for a time, only to find out that the love of her life, Richard Ruhr, had been having an affair behind her back all along.

She divorced him, got depressed, quit her job and by the time I grew to adulthood at the start of the game, she was living in a hippie commune somewhere on Mercury, trying desperately to get some gossip on her ex-husband.

New beta out now

This new beta is now available from the forum if you have purchased insider access (if you haven’t there’s still time.) Let me know if you find any other interesting stories such as these!

Read more