JavaScript Unit Testing with Jasmine

So I built a fairly involved single-page JavaScript / HTML app that generates litigation calendar events using some convoluted logic (calculating holidays, when those holidays are observed (e.g., if November 11th falls on a Saturday, Veterans Day is observed on the preceding Friday, but if it falls on a Sunday, it's observed the next Monday - CRC 1.11), etc. It’s large, and important, enough, I finally sat down and learned how to build Jasmine unit tests for JavaScript. My code may be spaghetti (I never really sat down to learn JavaScript, and kind of winged it with ES6 for this app), but at least it will be validated spaghetti.


I have a book, JavaScript Unit Testing, that covers Jasmine 1.2, so I’m using the ancient 1.3.1-standalone version here. It works, for now (I’ll learn the new stuff as I have time).

src/testCode.js
The code being tested. This is a silly, simple example:


function returnsTrue() { return true; }

spec/testCodeSpec.js
A “suite” is a group of test cases used to test JavaScript code, specified with describe():

describe( title of suite, function implementing test suite )

A “spec” is a test case within a suite, containing one or more expectations. All expectations must evaluate true or test case fails.

it( title of test case, function implementing test case )


The expection format is straightforward, where .matcher() is one of the list at the bottom (e.g., .toEqual(), .toBeLessThan(), etc):

expect( code_to_test ).matcher( [expected_value] )


Here’s a simple, silly example:

describe("Test suite", function() {
    it("returnsTrue() evaluates as true", function() {
        expect( returnsTrue() ).toEqual( true );
    })
});


 SpecRunner.html
The file that actually runs the unit tests, when opened in a browser. This file is supplied in the standalone distribution of Jasmine, and modified with the filenames of the source and test suite files (changes in bold, code omitted for brevity):

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
  "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
  <title>Jasmine Spec Runner</title>

  <link rel="shortcut icon" type="image/png" href="lib/jasmine/jasmine_favicon.png">
  <link rel="stylesheet" type="text/css" href="lib/jasmine/jasmine.css">
  <script type="text/javascript" src="lib/jasmine/jasmine.js"></script>
  <script type="text/javascript" src="lib/jasmine/jasmine-html.js"></script>

  <!-- include source files here... -->
  <script type="text/javascript" src="src/testCode.js"></script>
  <!-- include spec files here... -->
  <script type="text/javascript" src="spec/testCodeSpec.js"></script>
  <script type="text/javascript">
    (function() {
// [...]

    })();
  </script>
</head>
<body>
</body>
</html>


Expectations that can be supplied:

.toEqual( expectedValue )Passes if the value returned is equal to the value expected (including, e.g., arrays).
.toBe( expectedValue )Passed if the value returned is of the same type and value as that of the expected value (uses '===').
.toBeDefined()
Passed if the property or value is defined (var object = new Date(); ).
.toBeUndefined()
Passed if the property or value is undefined (var object; ).
.toBeNull()
Passed if the property or value is null (var object = null; ).
.toBeTruthy()
.toBeFalsy()

Passed when a value is true or false, respectively (var object = true; ).
.toContain( needle )
Passed if the haystack of the returned value contains the supplied needle. In a string, does a substring search. In an array, looks for an element that matches needle:
    expect( "Sunday, August 9").toContain("Sunday");
    expect( ["Dog", "Cat"] ).toContain("Dog");
.toBeLessThan()
.toBeGreaterThan()
Simple mathematical less-than / greater-than operations, respectively:  
    expect(4).toBeLessThan(5);
.toMatch()
Passed when a value matches a string or regular expression. I.e., check that parameter is a digit:
    expect( 420 ).toMatch("[0-9]+");
.not.matcher()
E.g. (and i.e.): .not.toBe(), .not.toEqual(), etc. Inverts the test.

There’s more to Jasmine, and I’ve only scratched the surface - of an obsolete version!

You can execute code before each test:

describe("CalendarLASC.js - Judicial Holidays", function() {
    var c;
   
    beforeEach(function() {
        c = new CalendarLASC();
    });

   
    // Judicial Holidays in 2015
    it("Thursday January 1, 2015 New Year's Day (2015)", function() {
        expect( c.isDateJan1( new Date(2015, 0, 1) ) ).toBeTruthy();
    });

//...
 
 And test suites can be nested:

describe("CalendarLASC.js - Judicial Holidays", function() {
    var c;
   
    beforeEach(function() {
        c = new CalendarLASC();
    });
   
    describe("2015 Dates", function() {
        // Judicial Holidays in 2015
        it("Thursday January 1, 2015 New Year's Day (2015)", function() {
            expect( c.isDateJan1( new Date(2015, 0, 1) ) ).toBeTruthy();
        });
 
        // ...
    });
    describe("2016 Dates", function() {
        // ...   
    });
//...

A failed test will look something like this:
Where the value on the left is what was actually returned by the tested code, and the value on the right is what the unit test was told to expect.

Comments