Our website is made possible by displaying online advertisements to our visitors. Please consider supporting us by disabling your ad blocker.

Unit Testing a Node.js Application with the Jasmine Testing Framework

TwitterFacebookRedditLinkedInHacker News

When building an application, regardless if it is mobile, web, or neither, it is a good idea to come up with a collection of tests that can be used in a continuous integration scenario. I must admit, I don’t have the best habits when it comes to writing tests, but I’m sure my applications would be significantly better if I did.

Previously I had written about unit testing in Golang as well as unit testing in NativeScript with Angular. This time around it makes sense to talk about writing tests in Node.js, another technology that I have in my tool belt.

Node.js is one of those technologies where there are a million different ways to solve a problem. That said, there are a few testing frameworks available, for example, Mocha with Chai, QUnit, and Jasmine. There is no wrong approach to this, but for this example we’re going to be focusing on Jasmine.

To get a better understanding of writing tests with Jasmine, it makes sense to start with a fresh project and work our way into it.

Create a new Node.js project with your Command Prompt or Terminal with the following command:

npm init -y

The above command will leave us with a package.json file. There are a few ways to get started with Jasmine. You could install it globally, or locally to the project as a development dependency. When it comes to continuous integration, it might be more logical to use it as a development dependency.

From the command line, execute the following command:

npm install jasmine --save-dev

Make sure that your command line path is the same as the project’s package.json file.

To use the Jasmine package, it can either be ran by using the long nasty path to the executable JavaScript file within the node_modules directory, or we can configure our project scripts to reference it.

Open the project’s package.json file and edit the scripts section to look like the following:

"scripts": {
    "test-init": "./node_modules/jasmine/bin/jasmine.js init",
    "test": "./node_modules/jasmine/bin/jasmine.js"
}

Notice we have a test-init and a test script. We’ve got a very empty project, so we need to initialize our Jasmine specs. To do this, execute the following:

npm run test-init

The above command will call our package.json script and leave us with a few directories and files within our project. Leaving everything it created as default will work for us.

Going forward, to run any Jasmine tests, we just execute npm test which will run our other package.json script.

Now let’s create our JavaScript application. Create an app.js file at the root of your project with the following simple code in it:

var add = (x, y) => {
    return x + y;
}

module.exports = add;

Above is a crazy simple function and a popular example for unit testing. Don’t worry, we’re going to add some complexity to our project, but it will get us started.

To test this function, we’re going to need to create a spec file. Create a spec/app.spec.js file in the project with the following code:

var Add = require("../app");

describe("Add functionality", () => {
    it("calculates that x + y = z", () => {
        expect(Add(4, 5)).toEqual(9);
        expect(Add(14, -5)).toEqual(9);
    });
    it("calculates that x + y != z", () => {
        expect(Add(14, -5)).not.toEqual(9);
    });
});

In the above example we have a single suite of several tests. The suite is like a grouping of tests, in this case our grouping is for testing the add function. Our tests are a little random in the above example, but we’re testing to see if our function result matches a desired number. We can have multiple expectations per every it chunk. The second test is to make sure the function doesn’t return a result that matches a desired input.

In our example, the first test will succeed, but the second will fail.

Now let’s look at a more realistic example, one using an API with HTTP and Express.

Before we can start using Express, it needs to be downloaded as a dependency within our project. From the command line, execute the following:

npm install express --save

Now change the app.js file to look like the following:

var Express = require("express");

var app = Express();

app.get("/", (request, response) => {
    response.status(200).send("The Polyglot Developer");
});

app.get("/test", (request, response) => {
    response.status(500).send({ "message": "This is an error response" });
});

var server = app.listen(3000, () => {
    console.log("Listening on port " + server.address().port + "...");
});

module.exports = server;

The above code will create an Express application with two API endpoints being served on port 3000. You can run this application and test it from your web browser.

To test our API, we’re going to need a way to issue HTTP requests. From the command line execute the following command:

npm install request --save

Now we’ll be able to request data from remote services, including our locally running service.

Open the project’s spec/app.spec.js file and make it look like the following:

var Request = require("request");

describe("Server", () => {
    var server;
    beforeAll(() => {
        server = require("../app");
    });
    afterAll(() => {
        server.close();
    });
    describe("GET /", () => {
        var data = {};
        beforeAll((done) => {
            Request.get("http://localhost:3000/", (error, response, body) => {
                data.status = response.statusCode;
                data.body = body;
                done();
            });
        });
        it("Status 200", () => {
            expect(data.status).toBe(200);
        });
        it("Body", () => {
            expect(data.body).toBe("The Polyglot Developer");
        });
    });
    describe("GET /test", () => {
        var data = {};
        beforeAll((done) => {
            Request.get("http://localhost:3000/test", (error, response, body) => {
                data.status = response.statusCode;
                data.body = JSON.parse(body);
                done();
            });
        });
        it("Status 200", () => {
            expect(data.status).toBe(500);
        });
        it("Body", () => {
            expect(data.body.message).toBe("This is an error response");
        });
    });
});

The above has significantly more going on than our previous example.

Before we jump into what our test is doing, we need to take a step back and figure out what Jasmine has to offer when it comes to testing. Remember, describe was for a test suite and it was for a particular test.

We also have access to things like beforeAll, beforeEach, afterEach, and afterAll. These are useful for test preparation and test cleanup.

The first part of our test suite looks like the following:

var server;
beforeAll(() => {
    server = require("../app");
});
afterAll(() => {
    server.close();
});

We are running our app.js file when we start our test, obtaining reference to our server variable, and closing our server when the tests have completed.

The first test suite represents all of our endpoints. Now we have a nested test suite that will represent tests for a particular endpoint:

describe("GET /", () => {
    var data = {};
    beforeAll((done) => {
        Request.get("http://localhost:3000/", (error, response, body) => {
            data.status = response.statusCode;
            data.body = body;
            done();
        });
    });
    it("Status 200", () => {
        expect(data.status).toBe(200);
    });
    it("Body", () => {
        expect(data.body).toBe("The Polyglot Developer");
    });
});

In this group of tests we issue an HTTP request against our endpoint before trying to test anything. Because it is an asynchronous event, we need to work with the done callback. The done callback says, don’t move along to each test until we’re ready.

Using the results from the request, we can test however we please. For example, it might be useful to test the status codes, response body, or specific errors.

Go ahead and use npm test to see it in action.

Conclusion

You just saw how to use Jasmine to test various points of your Node.js application. The tests can be against functions or API endpoints if you’re creating your own server.

There are other types of tests that can be run in your Node.js application, but that explanation is best saved for another day.

Nic Raboy

Nic Raboy

Nic Raboy is an advocate of modern web and mobile development technologies. He has experience in C#, JavaScript, Golang and a variety of frameworks such as Angular, NativeScript, and Unity. Nic writes about his development experiences related to making web and mobile development easier to understand.