Unit Testing Javascript with Jasmine

Jasmine is a framework for unit testing Javascript code. It has an easy syntax that allows fast creation of human readable, spec-like test code to verify the correct behavior of Java application code.

When creating a new npm project that will use jasmine for testing, specify “jasmine” as the test script:

$ npm init
...
Press ^C at any time to quit.
name: (vector2d) 
version: (1.0.0) 0.0.1
description: Library of 2D vector operations
entry point: (vector2d.js) 
test command: jasminegit repository: none
keywords: vector javascript
...

Then install jasmine, saving it as a dev dependency:

$ npm install --save-dev jasmine
vector2d@0.0.1 /Users/joedev/example-code/jasmine-test
|-+ jasmine@2.99.0 
  |-- exit@0.1.2 
  |-+ glob@7.1.2 
  │ |-- fs.realpath@1.0.0 
  │ |-+ inflight@1.0.6 
  │ │ |-- wrappy@1.0.2 
  │ |-- inherits@2.0.3 
  │ |-+ minimatch@3.0.4 
  │ │ |-+ brace-expansion@1.1.11 
  │ │   |-- balanced-match@1.0.0 
  │ │   |-- concat-map@0.0.1 
  │ |-- once@1.4.0 
  │ |-- path-is-absolute@1.0.1 
  |-- jasmine-core@2.99.1 
 
$ du -skh node_modules/
872K	node_modules/

With jasmine installed, invoking the “test” script with the “npm” command will run it:

$ npm test
 
> jasmine-test@1.0.0 test /Users/joedev/example-code/jasmine-test
> jasmine
 
Started
 
No specs found
Finished in 0.003 seconds

Running Jasmine via the npm test script with the “-v” option shows the version of npm instead of the version of Jasmine. But ‘npm list’ shows what’s installed in a npm project, including versions (limiting depth to 1 to avoid listing all package dependencies):

$ npm test -v
3.10.10    ...the version of npm!!!
 
$ npm list --depth 1
jasmine-test@1.0.0 /Users/joedev/example-code/jasmine-test
|-+ jasmine@2.99.0
  |-- exit@0.1.2
  |-- glob@7.1.2
  |-- jasmine-core@2.99.1
 
$ ./node_modules/.bin/jasmine -v
jasmine v2.99.0
jasmine-core v2.99.0

The npm “test” script is an alias for invoking the “jasmine” command under “node_modules/.bin” directory; invoking that command directly also shows the jasmine version (though the version of “jasmine-core” seems to be different between the two modes of listing, not sure why that’s happening!).

Jasmine has its own “init” command that sets up a config file and a directory structure where “test specs” (unit test files) are found. Running this with the ‘npm test’ script command:

$ npm test init
 
> jasmine-test@1.0.0 test /Users/joedev/example-code/jasmine-test
> jasmine "init"
 
$ ls
node_modules/	package.json	spec/
 
$ find spec
spec
spec/support
spec/support/jasmine.json

Doing an ‘ls’ shows that a “spec” directory has been added to the project, and that it has a “jasmine.json” config file under it. Unit tests will be added directly under the “spec” directory; running “npm test” will run any tests placed there. The “jasmine.json” config file just has some minimal config by default, we’ll leave that as it is for this demo.

Here’s an example of a minimal Jasmine test spec:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
describe('Demonstration Unit Test in Jasmine', function(){    var answer;
 
    beforeEach(function(){        answer = 42;
        console.log("Before each test, answer=" + answer);
    });
 
    it('should verify some basics about numeric values', function() {	var value;
 
	expect(value).toBeUndefined();
 
	value = answer;
	expect(value).toBeDefined();	expect(value).not.toEqual(0);	expect(value).toEqual(answer);
 
	// Special handling for non-integer values
	value = 1 / 3;
	// expect(value).toEqual(0.333)
        // Error: Expected 0.3333333333333333 to equal 0.333.
        expect(value).not.toEqual(0.333);
	expect(value).toBeCloseTo(0.333);    });
 
    // Put an "x" in front of "it()" to temporarily disable a test
    xit('should not run this test', function() {
	// Should not run
	expect(1).toEqual(2);
    });
});

A Jasmine test spec consists of:

  • a describe() function block wrapping the entire suite of tests
  • one or more it() function blocks, each an executable unit test
  • one or more expect() statements with “matchers” that test whether a condition holds after some code is executed
  • an optional beforeEach() function for setup common to all tests in the suite (there’s also an optional afterEach() function for cleanup after each test)

Notice that testing non-integer values takes special handling: this is because of the limited precision with which floating values are represented in computers. A test of a variable with a floating point value may or may not fail, depending on round off errors. Jasmine provides the “toBeCloseTo()” matcher to handle this situation: it will compare values up to a given number of decimal places. The default number of decimal places is 2, but a second optional argument can be specified for a custom number of decimal places, like “.toBeCloseTo(0.333, 3)”.

Notice also that an individual unit test can be disabled by putting an “x” in front of the “it()” operator. An entire spec could similarly be disabled by putting an “x” in front of the “describe()” operator. It’s not uncommon for a commercial application with good test coverage to have dozens or even hundreds of unit tests. An enhancement or refactoring might cause a number of unit tests to fail. Disabling unit tests like this can reduce the amount of noise while making code changes.

Similarly, preceding “it()” with an “i” (making it read “fit()”) will cause just that unit test to run, skipping all others (the “f” stands for “focused” testing). Similarly, changing “describe()” to “fdescribe()” will cause just that test suite to run, skipping all other suites. This is another way of isolating a unit test for development and debugging – unit tests should always execute quickly, but with a large number of tests in a mature application, this can be a valuable way of saving time when debugging a test.

Running the above test produces this output:

> jasmine-test@1.0.0 test /Users/joedev/example-code/jasmine-test
> jasmine
 
Started
Before each test, answer=42
.*
 
Pending:
 
1) Demonstration Unit Test in Jasmine should not run this test
  Temporarily disabled with xit
 
2 specs, 0 failures, 1 pending spec
Finished in 0.01 seconds

Change the value in the “toEqual()” test from 42 to 43 creates a unit test failure; this message is displayed at the end of the test output:

Failures:
1) Demonstration Unit Test in Jasmine should run a test
  Message:
    Expected 42 to equal 43.
  Stack:
    Error: Expected 42 to equal 43.
        at UserContext.<anonymous> (/Users/joedev/example-code/jasmine-test/spec/demo_spec.js:17:16)
 
1 spec, 1 failure
Finished in 0.014 seconds
 
npm ERR! Test failed.  See above for more details.

Jasmine Matchers

Jasmine provides a large number of matchers that can be used in the it() function of a test spec to test conditions in application code:

expect(fn).toThrow(e);
expect(instance).toBe(instance);
expect(mixed).toBeDefined();
expect(mixed).toBeFalsy();
expect(number).toBeGreaterThan(number);
expect(number).toBeLessThan(number);
expect(mixed).toBeNull();
expect(mixed).toBeTruthy();
expect(mixed).toBeUndefined();
expect(array).toContain(member);
expect(string).toContain(substring);
expect(mixed).toEqual(mixed);
expect(mixed).toMatch(pattern);
expect(number).toBeCloseTo(number[,precision]);
 
expect(...).not.toXXX(...);  ...negate the test

TL; DR

Here’s the 30 second synopsis of how to set up a npm project that will execute jasmine unit tests:

  • npm init – specify “jasmine” as the test script
  • npm install jasmine
  • npm test init
  • create a test spec (extension “.spec”) under the spec/ directory
  • npm test

A suite of test cases is enclosed in a call to the “describe()” function.

An individual test case is wrapped in a call to the “it()” function.

Jasmine provides a variety of matchers (“.toBeXXX()”) to test for valid states of variables in code.

Prefixing “it()” or “describe()” with an “x” will suppress that test or suite of tests.

Prefixing “it()” or “describe()” with an “f” will cause only that test or suite of tests to run (“focused” testing).

References

Jasmine Documentation
Creating a Jasmine Test Suite (Jasmine docs)
Jasmine Matchers (Jasmine docs)

Versions

$ node -v
v6.12.3

$ npm test -v
3.10.10

$ npm list –depth 1
jasmine-test@1.0.0 /Users/joedev/example-code/jasmine-test
|-+ jasmine@2.99.0
|– exit@0.1.2
|– glob@7.1.2
|– jasmine-core@2.99.1

$ ./node_modules/.bin/jasmine -v
jasmine v2.99.0
jasmine-core v2.99.0

$ node -e ‘console.log(process.versions)’
{ http_parser: ‘2.7.0’,
node: ‘6.12.3’,
v8: ‘5.1.281.111’,
uv: ‘1.15.0’,
zlib: ‘1.2.11’,
ares: ‘1.10.1-DEV’,
icu: ‘58.2’,
modules: ’48’,
openssl: ‘1.0.2n’ }

Add a Comment