Whenever testing code in the context of Node.js comes up in conversation, it’s very likely to be accompanied by an exploration of testing-related tooling and strategies. If you’ve ever participated in such a discussion, it’s not uncommon to hear one or more of the following terms thrown around: test runners, assertion libraries, mocking, stubbing, spying, and continuous integration. While these can certainly be valuable tools to consider as you build out a full test suite, the thought of familiarizing oneself with each can be intimidating to a developer new to the Node.js ecosystem or testing in general.

But you don’t need any of these tools to write a basic test. The idea behind a test is to make an assumption about your code that you’d expect to be true. In cases where these expectations are in fact untrue, the script should abort with a failing exit code. Typically the script is terminated by an uncaught error.

Let’s imagine we have a simple sum function which accepts two arguments where the return value is the sum of the two arguments.

function sum( x, y ) {
return x + y;
}

Our most basic test might simply consider a summing of two arbitrary numbers for which we’d expect the assumed value to be returned.

If we were to run this script, we’d see that it quietly finishes because the summed value is equal to our expected assumption. If we were to tamper with our original sum function by, say, changing the + operator to -, we’d see that it terminates with an uncaught error using the message we specified.

That’s really all there is to basic testing. We could optionally choose to add this as a script in our project’s package.json so that it could be predictably run through the npm test command.

As you continue to build more tests, you’ll want to consider the topics listed in the first paragraph, as they’ll help to extend and simplify common usage patterns. A brief description of each is included below:

Test runners provide a means to combine your tests into a logical grouping, and often provide a reporting interface to more easily interpret the results of your tests.

Mocking, stubbing, and spying are related topics which allow you to create simulated functions which can then be observed, granting you insight into how your code is executed without the need to actually execute it.

Continuous integration is a service which will automatically run tasks (including tests) when the code is changed.

Grunt is a JavaScript task runner that helps automate common development tasks such as minifying and testing code. Traditionally, using Grunt is a two-step process. First you install Grunt’s command-line interface (GLI) globally using npm (npm install -g grunt-cli). Next, in any project where Grunt is to be used, you must locally install Grunt and any Grunt plugins you wish to include. The command-line interface is installed separately so as to allow multiple versions of Grunt to be installed on the same machine, and acts as a simple wrapper for executing tasks through the local Grunt module.

Once you’ve installed Grunt both locally and globally, and after configuring your Gruntfile, running a task is as simple as passing a task name to Grunt’s command-line interface. For example, if my Gruntfile defines a test task, I could run it by entering grunt test into my command-line. It may sometimes be necessary or handy to be able to trigger a task without the Grunt CLI, in which case you can use the following code snippet, replacing test with the task(s) of your choosing:

node -e "require('grunt').tasks(['test']);

There are a few common scenarios in which using the above syntax would be preferable to running tasks through Grunt directly. Specifically, this may be a requirement in cases where another developer or system does not have Grunt installed globally.

As an illustration, consider the scripts feature of npm. Often overlooked, the scripts section of a package.json allows a developer to define command-line scripts to be run via predefined keywords. For example, you could trigger a Grunt test task with the following configuration, to be called using npm test.

The advantage of using the command above in place of grunt test is that your test script can now be executed regardless of whether the global Grunt module is installed.

Removing global dependencies may also help in the case of continuous integration services. Personally, I use Travis CI to run unit tests against my GitHub project commits. Up until recently, I’ve used the before_script section of my .travis.yml configurations to install the global Grunt module. By running my tasks directly, I can safely omit this configuration section. Other services may not even grant you the option to configure global dependencies, so this may be your only option.

And it’s not just Grunt, either. Where possible, make an effort to eliminate the need for global dependencies in your npm scripts. Not every developer is going to be familiar with the tools that you choose to use, and removing such dependencies helps to avoid frustration caused by error messages. It may someday be possible to define global dependencies in a project’s package.json, but that’s not likely to be coming anytime soon.

Recently, I’ve been toying with JavaScript’s setInterval method which, if you’re unfamiliar, allows you to execute code repeatedly at a specified time interval. For example, one might create an interval which executes every second by using the following code snippet:

Both setInterval and its close cousin setTimeout suffer from latency caused by JavaScript’s single-threaded nature. While you may intend for an interval to execute every 1000 milliseconds, in reality it could take slightly longer for the function to be triggered. This is typically only a few milliseconds and therefore might appear to be a negligible problem. You might also expect that subsequent intervals would make an effort to get the code execution back on a predictable schedule. In other words, if there was 1007 milliseconds between registering the interval and the first code execution (a delay of 7 milliseconds), you could expect the next execution to occur as close to 2000 milliseconds as possible (i.e. 993 milliseconds later).

Depending on your browser, however, this may not be the case. We can demonstrate this by tracking the number of milliseconds which have passed since setInterval was registered:

If you run this in current versions of Chrome, Safari, Internet Explorer, or Node.js, you’ll notice that the interval execution grows increasingly out of sync with the original setInterval function call.

In my testing, I’ve found that only Firefox attempts to keep the interval execution in sync.

Regardless of whether this is the intended behavior of setInterval, I needed a means by which I could execute code as closely as possible to a predictable interval. Below is my solution to this problem:

Including the code sample above will add a new setCorrectingInterval function to the window global that can be called using the same parameters you would normally pass to setInterval. Here’s a detailed breakdown of what’s going on in the new function:

To track properties related to this particular interval instance, we wrap the inner tick function inside a closure construct.

When the function is first called (i.e. when instance.started is false), a number of properties are stored to our instance object.

In place of setInterval, we repeatedly call setTimeout, passing the tick function and an adjusted delay.

The adjusted delay is calculated by tracking both the start time of the original function call and an incremented target execution time.

To achieve the desired behavior, we can update the broken example to use setCorrectingInterval in place of setInterval.

RequireJS is a JavaScript utility for module loading and dependency management. It enables you to keep independent modules in separate files, and automatically loads the defined dependencies between them. This gives the notion of an “import” and, in theory, leads to more maintainable JavaScript.

Modules can be thought of simply as blocks of code executed under the context of the namespace in which it is defined. By default, this is the filename without the extension. In the example below, calling define in the dinner.js script will create the dinner module. The return value of a module can be used by any other code which depends upon it.

It is important to note that keeping the JavaScript files separated will negatively impact your page load speed. With a RequireJS workflow, you will want to use an optimization tool (r.js) during your production deployment process to concatenate and minify your code to a single, small file. You can read more about this in the “Further Reading” section below.

An Example

Let’s take a look at a very simple example of RequireJS in a web application.

Imagine we have the following project structure:

index.html

js/

main.js

dinner.js

meat.js

potatoes.js

require.js

We will want to start by adding a script tag to our main page to load the RequireJS library. We will also specify an entry point through the data-main attribute.

This entry point will load the js/main.js script automatically. From our RequireJS scripts, we can either define a new module or use require to load dependencies without defining a new module. In either case, loading dependencies is done using the function call, as seen below in dinner.js and main.js:

To load other modules as dependencies, pass an array as the first parameter with a series of modules to load. In the main script, loading ‘dinner’ will call the dinner.js file, which returns an object containing the totalCalories property. Since our meat and potato modules do not depend on anything else, we can omit the first parameter. In the main script, I use require because I want to depend on modules, but I do not want to have main itself be used as a dependency elsewhere. Use the define method instead when defining a module. By default, the module name takes the name file minus the extension, so you should only define a single module per file.

There are several ways to call the define and require methods, including the option to specify a custom module name. To learn more, read the API documentation for define.

Further Reading

For a more thorough walkthrough of RequireJS, read through the API documentation. For instructions on how to concatenate and minify your RequireJS projects, refer to the optimization instructions. To simplify the build process, you may find that using a tool like grunt.js can be very helpful.

RequireJS is extensible, so there are a variety of plugins you can use to extend the capabilities of the module loader. For example, if you want to load CoffeeScript, you can use the require-cs plugin. If you use a templating library like mustache.js, you can use the text.js plugin to load static text resources to avoid writing your templates as JavaScript strings or between “ tags.

To see how RequireJS is used in an existing project, check out my “Toupée” project on GitHub, which uses RequireJS with Backbone.js.

Conclusion

RequireJS removes the guess work from dependency management, meaning you can worry less about the order your scripts are loaded and where you define your functions. By separating your modules into separate files, you create a separation of concerns and avoid the possibility that your application’s main script grows too large to manage.