email@encode8.com
JavaScript

A Beginner’s Guide to JavaScript Unit Testing with Jasmine

Unit testing is an integral part of the development workflow. Most people hate to write unit tests. But these tests ensures security of your code. With unit testing, bugs are detected early in the development process. Code-base becomes more maintainable and easy to grasp. The purpose of unit testing is to ensure that your code runs as expected. Under any condition, in any machine.

What is unit testing?

mycodecantfail

Every large piece of code can be broken down into small parts. For example, if you have a file that defines helper functions, each helper function is a small part of the whole file. These smaller parts of code can be treated as a ‘unit.’ By unit testing, we the developers ‘test’ that these smaller parts of code are working fine; they perform as designed, their return values are exactly what we have foreseen. In other words, unit testing performs tests on individual parts of codes to verify that the results are as expected.

Why is it necessary?

Unit testing is an important part of the development workflow. It allows you to make changes in your code-base and validate that nothing is broken. For example, you have added a new feature to your site. You write a couple of tests and see that this new code is working as designed. Now, you want to test if all other old features are also working. Just run the old tests and if they pass, you are good. This is the single most important benefit that unit testing offers.

Now, you may be wondering that writing tests can be a daunting task. You have already written few hundred lines of code that runs the business logic; writing tasks is another time-taking affair. In fact, it is the opposite: writing tests is incredibly simple. Since small parts of code is subjected to unit-testing, the tests are equally small and easy to write.

unit-testing-comic-funny

The other benefit is that unit-testing comes handy when you are fixing a bug. As soon as you find a bug, write a test for the bug, and then fix the bug. Now, the test will pass because you have fixed the issue. This bug will never occur and you can prove that by running this test.

Unit-testing is also very useful for developer collaboration. Let us assume you have a large code-base which you maintain alone. Now, another developer joins in. This new developer will find himself overwhelmed by the size of the code-base. Unless you have written unit-tests. By going through the tests, our new developer would have a better understanding about the practical details of the code-base.

How do unit tests work?

Unit-tests work on three basic ideas: Arrange, Act, Assert. To be able to test code, you need to run the code with some data. To supply this data is to arrange. If you want to drive your car, you need fuel for that. Or if you want to make a call from your cellphone, you need network for that. To act is to execute the code which is subjected to unit tests. In our examples, driving the car or making the call is the act. Finally we need to ‘test’ if the result is as expected. This assertion part ensures that your expectations are met. For example, reaching the right destination or being able to connect with the right person, is what assertion is.

In pseudo language,

function myFunc(a,b){
   do somthing;
   return something involving a and b;
}

// Arrange
let myVar, x, y;

// Act
myVar = myFunc(x,y);

// Assert
Assert.that(myVar, isEqualTo(expected_result));

Jasmine Unit Testing Framework

jasmine javascript framework

Now that we have a basic understanding of unit testing, it is time we get our hands dirty and try out one of the many frameworks. Jasmine is one of the most popular Javascript unit testing frameworks. It has a very small footprint. Jasmine is open-source and distributed under MIT license.

Installation

To install Jasmine via npm, you can run:

npm install jasmine --save-dev

To initialize Jasmine, run:

node node_modules/jasmine/bin/jasmine.js init

When you initialize Jasmine with the above command, Jasmine creates spec and spec/support directories, and spec/support/jasmine.json file. If you look inside the jasmine.json file, you’d notice this is where configuration settings are saved.

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

spec directory is where the unit test files will the saved. These test files are to be saved in the format filename.spec.js for Jasmine to identify. The other configuration options are beyond the scope of this beginner’s guide. (If you are interested, you can read more in the Configuration section on this page.)

Next, add the following in your package.json file:

"scripts": {
  "test": "jasmine"
}

The above code lets you run npm test command and use Jasmine as the testing framework.

Functions to test

Let us create a few functions that we want to test. Create a new file formulas.js in the root directory (i.e. the directory containing spec directory). Add the following few functions, as an example:

function sum(a,b){
  return a + b;
}

function subtract(a,b){
  return a - b;
}

function multiply(a,b){
  return a * b;
}

function divide(a,b){
  return a / b;
}

module.exports = { sum, subtract, multiply, divide };

These functions are very straight-forward in nature, and does not require much explanation. Note that we have module.exports. With this, we export the functions as an object so that we can call each function when called after require statement. If this does not make much sense, it is alright; it will be clear in the next step.

Writing tests

Next, create a new file formulas.spec.js inside spec directory, and add these lines:

var formulas = require('../formulas.js');

describe('Formula test', function(){

  it('adds two numbers', function(){
    expect(formulas.sum(1,2)).toBe(3);
  });

});

So what’s happening here?

  1. First, we include the functions we defined in the previous step, by calling require('../formulas.js') (Note that the .js part is optional.)
  2. Then we create a suite of tests by using describe function. describe function is a handy way to separate groups of related tests. In our case, we are testing a bunch of simple mathematical formulas; so we group them by Formula test. The second argument in describe function (i.e. function()) will contain the individual tests.
  3. Next, we start our tests with the sum function defined earlier in formulas.js. We start with it function. This function takes a first argument, as a string, which is a short description of what the function does.
  4. The second argument of it is where we actually test. expect function, here, arranges the value to test, formulas.sum acts, and finally toBe function asserts. The function names are self-explanatory in nature, so I am not going into details here.

Running the tests

Now that we have written a test, it is time we run them. Just run npm test from our root directory (i.e. the directory containing spec directory). The output will be similar to this:

> js_test@1.0.0 test /path/to/directory
> jasmine

Randomized with seed 42589
Started
.


1 spec, 0 failures
Finished in 0.01 seconds
Randomized with seed 42589 (jasmine --random=true --seed=42589)

Note it says: 1 spec, 0 failures. This means our test passes.

For our next test, let us choose multiply function. Add the following inside describe block, in formulas.spec.js:

it('multiplies two numbers', function(){
  expect(formulas.multiply(2,3)).toBe(8);
});

Note that we have deliberately taken 8 to be the product of 2 and 3. If you run npm test again, output will be similar to this:

> js_test@1.0.0 test /path/to/directory
> jasmine

Randomized with seed 96799
Started
.F

Failures:
1) Formula test multiplies two numbers
  Message:
    Expected 6 to be 8.
  Stack:
    Error: Expected 6 to be 8.
        at 
        at UserContext. (/path/to/directory/spec/formulas.spec.js:9:35)
        at 

2 specs, 1 failure
Finished in 0.016 seconds
Randomized with seed 96799 (jasmine --random=true --seed=96799)
npm ERR! Test failed.  See above for more details.

So, we have 2 specs, 1 failure. Note that the Failures section describes test failures as: Formula test multiplies two numbers. This a combination of first arguments of describe and it functions. So, always be ‘descriptive’ in choosing these arguments so that failure messages make sense.

Similarly, write two more tests for subtract and divide functions.

Conclusion

This is an introductory text on unit testing with JavaScript. It barely scratches the surface. There is a lot more you can do with unit-testing, even mocking AJAX requests. Make sure you read Jasmine’s Documentation which explains in detail how unit testing works. There are several other JavaScript unit-testing frameworks which may fit your specific requirement.