Friday, February 7, 2025

Continuous TDD in JavaScript with Node.js and Gulp

Anyone who practices TDD in their day-to-day work knows the benefits of a cleaner code that is tested and easy to modify. These benefits, however, do not come automatically — it takes a lot of study and practice to understand the technique well and to know how to apply it.

Consider the recommendation to take short steps, that is, to test the code bit by bit. The great advantage of that is when a problem occurs, the exact point of the occurrence is immediately known. However, very short steps can be annoying, with constant interruption of flow — time to change the window, give the command to run the tests, see the result and then resume the thread of the skein. Many times, what happens is that the developer stops following the recommendation and, thus, gives up the benefits.

Is the problem with the programmer or the development environment itself? In this article, you will learn a way to practice TDD in JavaScript that streamlines the visualization of results and promotes continuity in the workflow. You only need to save the edited file to see test results immediately.

As the current framework for working with JavaScript is Node.js, in this article we will work with that and the NPM repository. And to help us with the tests we will use Gulp, which is a task automator.

TDD Continuous: Responsiveness

Practicing Continuous TDD means you will see the result of running your project tests without having to leave the development environment and execute a specific command. The command will run automatically as soon as you save the file you are editing. Configure your environment using the tools described in this article and you will notice the difference in productivity.

Node.js is a JavaScript execution environment that runs directly on the operating system, usually on servers. It allows you to run JavaScript code outside the browser, expanding the breadth of the language.

NPM is the Node Package Manager. It makes it easy to download and install third-party tools and libraries. It is similar to NuGet (.NET), Maven (Java), and RubyGems (Ruby).

Jasmine is a framework for running automated tests that can be used in any JavaScript environment, whether inside or outside the browser. It allows you to specify the expected behavior of a system in the same way as the rspec (Ruby) tool.

Gulp is a project building automation tool, like Make (C), Ant and Maven (Java), and Rake (Ruby). Gulp enables you to write scripts to perform common tasks in the JavaScript world, such as obfuscating and minifying files, converting from SASS/LESS to CSS and converting from CoffeeScript to JavaScript. In the context of Continuous TDD, Gulp is useful for observing the file system and triggering the execution of tests when changes occur in existing files.

Gulp-jasmine is a plugin that lets you start Jasmine from Gulp. Generally, Jasmine is used in conjunction with the browser and in this article, it will run over the Node in a terminal window.

Browserify is a module manager for JavaScript that allows you to use the same standard of definition and export of modules adopted by the Node in the browser. This makes it possible to run the code written originally for the Node in the browser, without any modification.

Creating a Project from Scratch

In this section a JavaScript project will be created from scratch that will demonstrate the configuration of the tools used to practice Continuous TDD.

Let’s start by opening a terminal window and executing the commands described to install Node.js and NPM, according to the types of operating systems.

In Ubuntu or derivatives, use the following code:

sudo apt-get install nodejs-legacy
# if nodejs-legacy is not available, switch to nodejs
sudo apt-get install npm

On other operating systems, go to the Node page, download and install the appropriate version. The NPM will be installed along with the Node.

To install the Gulp package globally, use the following code:

sudo npm install gulp -g

If you’re on Windows, omit the word sudo.

Now we need to create a directory named example1 for the new project and access it. On any operating system, use the following commands to do this:

mkdir example1
cd example1

Then start a new project with NPM using the following command:

npm ini

This command creates a package.json file, which is similar to the Maven (Java) pom.xml. It describes the basic data of your project as well as the dependencies of external packages.

You will be asked to complete various data, such as project name, version, description and license, but, for the purpose of this article it is not necessary to fill in any of the data. You can simply press Enter on all questions to use all the default values.

Install the gulp and gulp-jasmine packages locally using the following command:

npm install gulp gulp-jasmine -- save-dev

This command installs the gulp and gulp-jasmine tools locally, that is, only in the current project, in a directory called node_modules. The -- save-dev part instructs npm to update the package.json file, adding gulp and gulp-jasmine as project dependencies at development time. This allows you to download and reinstall the dependencies at any time in the future just by running the npm install command. This is useful if you do not want to add dependencies to version control. If you use Git, for example, you can add node_modules to the .gitignore file, avoiding versioning of external dependencies.

You need to create a Gulp configuration file at the root of the project. To do this, create the src folder with the following sequence of commands:

mkdir src
cd src
mkdir spec
mkdir prod
cd .. 

The file that configures Gulp execution is called gulpfile, so at the project root, create a gulpfile.js file with the following content:

var gulp = require('gulp');
var jasmine = require('gulp-jasmine');

var sourceCodePath = 'src/**/*.js';

gulp.task('test', function() {
gulp.src(sourceCodePath)
.pipe(jasmine());
});

gulp.task('continuous-tdd', ['test'], function() {
gulp.watch(sourceCodePath, ['test']);
});

process.on('uncaughtException', function(e) {
console.error(e.stack);
});

This file defines two tasks for gulp:

  • test: this task runs the tests once and ends;
  • continuous-tdd: this task runs the tests once and then looks at the file system and re-runs the tests automatically with each modification. The process.on('uncaughtException') statement instructs Node.js to, instead of terminating the program in the occurrence of exceptions, just write them on the screen. This is useful for keeping Gulp running even in case of compilation errors. This solution is acceptable in this case because the Node is being used only as an auxiliary development tool. To host an application on Node in production, this type of solution should not be used.

Once the project structure is created, the JavaScript files will be written. For this example, imagine a class representing a tree, which should offer five fruits. First, a specification for trees will be written. Then the Tree class will be implemented. This will be done in very short steps, with continuous checking to see if the code is correct. In a dynamic and interpreted language such as JavaScript, this makes a big difference: there is no compiler available that can point to many programming errors, such as in Java or C#. Thus, the automated test becomes the detector of these errors.

The specification code (that is, the test code) will be placed in src/spec and the production code (that is, the one that implements the functionality) in src/prod. Open a text editor and create the files TreeSpec.js and Tree.js shown below, respectively.

Contents of TreeSpec.js

var Tree = require('../prod/Tree');
describe('Tree', function() {
it('must have 5 fruits', function() {
expect(new Tree().getFruits().length).toBe(5);
});
});

Contents of Tree.js

function Tree() {
}
module.exports = Tree; 

The TreeSpec.js file contains the Tree class specification, while the Tree.js file defines a Tree class, without any property or method yet. The last line containing the module.exports excerpt tells Node.js that module exports the Tree class. In a Node project, each JavaScript file represents a module and each module must export to others that it wants to make visible. In this case, the Tree.js file exports the Tree class and the TreeSpec.js file, when you call the require('../prod/Tree'), you get the Tree class.

Now, there is already a specification for the Tree class and a sketch of its implementation. On a terminal, in the project root directory, run the following command:

gulp continuous-tdd

The only existent test in the project will be executed and the result will be:

TypeError: Object #<Tree> has no method 'getFruits'

Gulp will continue to run, waiting for changes in the file system to run the tests again.

The next step is to implement the getFruits()> method in the Tree class. The goal is for the tree constructor to create a vector of fruits and store it as property of the object.

Practicing Continuous TDD implies simultaneously visualizing the code and the execution of the tests. To do this, make your text editor window and the terminal window side by side visible. If you are on Windows or Linux Mint, you can do this as follows: click the editor window and press Windows Key + Left Arrow; click on the terminal window and press Windows Key + Right Arrow. The windows will be arranged side-by-side conveniently.

Modify the Tree.js file to conform to the following:

function Tree() {
}
Tree.prototype.getFruits = function() {
return new Array(5);
};
module.exports = Tree; 

Once you’ve saved the file, observe at the console a message saying that the test is automatically executed with success. You’ll see a message like “1 spec, 0 failures”.

In addition to what has been shown, some improvements are possible. One of them is the use of the external gulp-watch package to detect the creation of new files (not only the modification of the existing ones). You can also use the watchify package to invoke Browserify automatically with each change, simplifying frequent browser testing.

About the Author

Diogo Souza works as Java Developer at Fulcrum Worldwide and has worked for companies such as Indra Company, Atlantic Institute and Ebix LA. He is also an Android trainer, speaker at events on Java and mobile world and a DevMedia co

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Popular Articles

Featured