/ ECMA2015

Using Jasmine with JavaScript / ES2015

Testing is an important aspect of development and in this article we'll take a look at how to add unit tests to a JavaScript (ES6 / ES2015) code.

Unit Testing
Units are the smallest pieces of functionality that we can test against in our code and henceforth the name unit tests. We will test individual pieces of code.

Jasmine

Jasmine is a behaviour-driven development framework that allows us to test our JavaScript code. Jasmine comes with a clean syntax that is very close to a natural language.

Jasmine can be used using a number of programming languages but today we are going to take a look at how to use it with Node.js

Installation

In order to get started we need to make sure that we install Jasmine and we can do this via npm by executing: npm i jasmine (we can also install it globally by using the -g flag.)

Configuration

Jasmine takes a configuration via a file called jasmine.json. This file can be created either manually or via jasmine's init command which we can invoke by typing in jasmine init in the command line.

Please note that if you don't have Jasmine installed globally on your system you'll need to call init from the local Jasmine install which you can do by executing node_modules/jasmine/bin/jasmine init.

This init command will then create the following structure:

|-spec
   |---support
       |---jasmine.json

With jasmine.json having the following default content:

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

The above configuration file is really simple - Jasmine is expecting all our specs (i.e. tets) to be in a spec folder and have any filename as long as it ends with spec (either lower or upper case s) and has a .js extensions.

Suites, Specs and Expectations

Let's discuss some key terminology around Jasmine. When creating a test, we'll first create a suite by calling the describe method, which takes two arguments: a string that specifies what the test is going to be about and a function, where we can specify our specs.

Specs are really just tests, which we can create by using the it method, which again takes a string and a function.

Inside that function is where we specify our expectations - an expectation which can either return true or false based on an actual value and an expected value.

Test Driven Development
TDD or Test Driven Development is a methodology whereby developers create tests first, without writing a single line of code. They'd then run these tests, expect each test to naturally fail, and develop code to fulril the tests. We are not going to follow this approach - we'll write some code first and then create the tests.

Example

Let's start by creating a relatively simple ES2015 code:

class Warrior {
  constructor(name, health, weapon) {
    this.name = name;
    this.health = health;
    this.weapon = weapon;
  }

  getWeapon() {
    return this.weapon;
  }

  takeDamage(damage) {
    return this.health - damage;
  }
}

export default Warrior;

And let's create our first test as well - we need to place this in the spec folder and remember, it has to have a name that contains the term spec in it (I'm calling my file warrior-spec.js):

import Warrior from './../warrior';

describe('Warrior Unit Test', () => {
  let warrior;
  beforeEach(() => {
    warrior = new Warrior('Dave, the Nomad', 100, 'sword');
  });

  it('should return warrior weapon', () => {
    expect(warrior.getWeapon()).toBe('sword');
  });
  it('should decrease warrior health by 10', () => {
    expect(warrior.takeDamage(10)).toBe(90);
  });
});

The above code specifies one test suite with two specs - we expect the warrior's weapon to be a 'sword' and we expect that if the warrior takes 10 damage, he'll have 90 health left out of a total 100.

Notice how easy it is to understand the code as it can be translated to pure English.

I'd like to draw your attention to a few things:

  1. beforeEach() is a function (as it's name suggests) that allows us to do something before each spec we specify - in this case we are instantiating the Warrior class. Doing this means that we can access this warrior variable in every spec - otherwise we'd have had to instantiate this class in every single spec which would have been a tedious job. You can also declare and assign values to variables here.
  2. Notice that we are using the import keyword, which is - at the time of writing this article - is not supported in Node.js therefore we need to do some transpilation of our code and we'll use Babel to achieve this.

Babel to transpile

In order to make sure that we run code that is being understood by Jasmine we need to transpile this code using babel and in fact we will be using babel-node.

As I'm sure you already know the drill - first we need to install babel along with the ES2015 preset from npm: npm i babel-core babel-preset-es2015 --save.

We also need to tell babel to use the ES2015 as a preset - please go ahead and create a .babelrc file with the following content:

{
  "presets": ["es2015"]
}

A preset in Babel is a set of plugins that are used to support a particular language - in our case it's going to be ES2015.

Let's also add a run.js file where we'll setup Jasmine to execute the tests:

import Jasmine from 'jasmine'

const jasmine = new Jasmine();
jasmine.loadConfigFile('spec/support/jasmine.json');
jasmine.execute();

Once this is done we can run our tests by executing the following command: babel-node spec/run.js.

The result should be the following few lines in the cli:

Started
..


2 specs, 0 failures

Perfect, we have passed all our tests.

Please remember Babel is only required because we are using some ES2105 specific code. Otherwise we could have simple used Jasmine on it's own without any special configuration.

Tamas Piros

Tamas Piros

Tamas is a full stack web developer turned technical trainer who has a decade of experience working with prestigious organisations and has delivered training classes all over the world.

Read More