JS Testing with Jasmine

Posted on - Last Modified on

Over the last few years, Node.js was (and still is) one of the most widely used JavaScript frameworks for Web development. Since more and more projects are using it, the business logic that gets implemented in projects that use Javascript is getting more complex. For complex business logic, automated testing and BDD (behavior driven development) is a key point for creating a quality product.

Jasmine is one of the BDD frameworks for JavaScript. The reason why many people choose this framework is because there is no need for any additional JavaScript library (besides the jasmine library) to create and run the tests. There is also no need for a DOM structure.

This article covers the installation steps of the Jasmine framework. I will present how to write test cases and show how to programmatically run tests from node. All the source code used in this article is available on GitHub.

Installation

Because I will use a node project for writing the business logic and for running the tests, I will start with creating a new node module.

When the npm init has finished, I create the index.js file, which is the main entry point for our node project. After that, I install jasmine using npm and initialize a new jasmine project inside my node module.

The jasmin init command creates the spec folder, where all the tests for the project should be placed. There is another folder inside the spec folder called support, which holds a configuration file called jasmine.json. This file has all the necessary configuration settings for running jasmine tests programmatically.

What to test?

In the project, I have created a file called mathFunctions.js that holds the business logic that I write tests for:

function isEven(number) {
    return number % 2 === 0;
}

function isOdd(number) {
    return !isEven(number);
}

function sum(numbers) {
    if (!numbers) {
        throw new Error("Parameter [numbers] should be NULL.")
    }
    if (numbers.length) {
        var sum = 0;
        for (var idx = 0; idx < numbers.length; ++idx) {
            sum += numbers[idx];
        }
        return sum;
    }
    return 0;
}

module.exports = {
    isEven: isEven,
    isOdd: isOdd,
    sum: sum
};

I have three functions: isEven, isOdd, and sum. The sum function should get an array as parameter and returns the sum of the items inside the array. The function returns an error if it gets a null value as parameter. The logic of isEven and isOdd methods should be self-explanatory.

What else do I need to know before writing the tests?

Jasmine test projects have three main components that help the developer to wright good tests.

The Test Suits

Every test suit within Jasmine should start with the describe method.

describe("My Test Suit", function() {
  // Logic for the test cases
});

The describe method should receive two parameters, a string that describes what the test suit covers, and what is tested, as well as a function for holding all the logic for the test cases.

The Specs

Test cases or specs can be defined using the it function. This function also receives two parameters: a string, and a function. The first parameter should describe the logic that we want to test.

describe("Boolean logic validation", function() {
  var a;
  var b;

  it("true and false", function() {
    a = true;
    b = false;
    expect(a && b).toBe(false);
  });
});

The Expectations

In the example above, inside the it method there is the expect function, which holds the actual value that I want to test. The expect functions should always be followed by a matcher function, which, in this case, is toBe. The toBe matcher behind the scenes does the comparison using the === operator (if not sure what is the difference between == and ===, check out my related article Common JavaScript Mistakes).

If I read out loudly the definition of the spec (or test): Boolean validation logic, true and false expect to be false, it’s almost the same as the code but in a format that can be easily understandable for non-developers.

Jasmine in Action

I set up the Jasmine framework inside the index.js file so I can run it programmatically:

var Jasmine = require('jasmine');
var jasmine = new Jasmine();

jasmine.loadConfigFile('spec/support/jasmine.json');

jasmine.onComplete(function(passed){
   if(passed){
       console.log('Success');
   }
    else {
       console.error("Failed");
   }
});

jasmine.execute();

First, I load the Jasmine framework and create a new instance of the Jasmine object. Then I load the configuration file from spec/support folder. The jasmine.json config specifies a filter for the files so the jasmine framework knows what files to execute and run the tests from:

{
  "spec_dir": "spec",
  "spec_files": [
    "**/*[sS]pec.js"
  ],
  "helpers": [
    "helpers/**/*.js"
  ]
}

The config file contains standard JSON with the fields spec_dir and spec_files. As a good practice, the spec files should be appended in the Spec word in the naming. For example: mathFunctionsSpec.js.

After setting the configuration file inside index.js, I set up a new handler for the onComplete event, which will be invoked when all the tests are executed. The last line of index.js invokes the test execution.

Where are my tests?

Inside the spec folder I have the mathFunctionsSpec.js.

var mathFunctions = require('../mathFunctions');

describe("Tests for my math functions", function () {


    it("2 should be even", function () {
        expect(mathFunctions.isEven(2)).toBe(true);
    });

    it("3 should not be even", function () {
        expect(mathFunctions.isEven(3)).toBe(false);
    });

    var myArray = [3, 2, 2, 3];
    var myArray2 = null;
    var myArray3 = [0.003, 0.004, 0.0009, 1, 3.2];

    it("3+2+2+3 to be 10", function () {
        expect(mathFunctions.sum(myArray)).toEqual(10);
    });

    it("For Null array expect to throw error", function () {
        expect(function () {
            mathFunctions.sum(myArray2)
        }).toThrow(new Error("Parameter [numbers] should NOT be NULL."));
    });


    it("For decimal numbers to have at least 2 precision correct", function(){
        expect(mathFunctions.sum(myArray3)).toBeCloseTo(4.108, 2);
    });
});

I import the mathFunctions.js from my project so I can use its methods inside the tests. I then test the isEven method first to return a truthy then falsy values.

After that, the three arrays (myArray, myArray2, myArray3) are defined, which I use for my next three tests. I then test if the sum method returns the correct sum of the values inside myArray.

The next test checks if an error was thrown with the correct error message “Parameter [numbers] should NOT be NULL”. Here, I use the toThrow matcher function, which should be used for error comparison and compares the error’s message too. 

In the last test, I used the toBeCloseTo matcher, which has two parameters: the expected value and the precision. In the example above, I said that the expected value should be close to two decimals compared to the actual value.

How do I run the tests?

The tests can be executed using the node index.js command. The screenshot shows that I have 2 failing tests. From the error messages, it can be seen that jasmine offers good information about the expected and actual values. It also addresses the details so the developer can easily find the error.

Posted 24 September, 2015

Greg Bogdan

Software Engineer, Blogger, Tech Enthusiast

I am a Software Engineer with over 7 years of experience in different domains(ERP, Financial Products and Alerting Systems). My main expertise is .NET, Java, Python and JavaScript. I like technical writing and have good experience in creating tutorials and how to technical articles. I am passionate about technology and I love what I do and I always intend to 100% fulfill the project which I am ...

Next Article

SydStart Speaker Spotlight: Niti Shah