The API created in the previous step did not have any tests. It is now time to start to rectify that.

As before, in addition to the example code below, the full code for the tests is available.

Adding Jasmine specifications

The tests are written in Jasmine, a behavior-driven development testing framework. The tests are written as specifications in which you:

  • describe – Name the thing (module/class/function) being tested
  • it – Enumerate the scenario under testing
  • expect – Assert that everything is as it should be after calling the system under test

This allows for a very readable set of specifications that describe how the system should function. For instance, adding together the describe and it sections to read “Policy… valid… should be true when all fields are present” provides an easily understood description of the scenario being tested.

Adding Jasmine

The Jasmine-Node package wraps Jasmine and exports it as a Node module. This allows Jasmine to be added alongside all of the other defined dependencies. The one difference is that it’s added as a devDependency rather than a regular one (to allow differentiation between what dependencies are required when the API is used).

// ...
"dependencies": {
  "express": "4.6.x",
  "mongoose": "3.8.x",
  "body-parser": "1.5.x"
},
"devDependencies": {
  "jasmine-node": "1.14.x"
},
// ...

Example specification

The valid function from the PolicySchema provides a fairly basic example for code needing tests.

PolicySchema.methods.valid = function() {
  if (!this.policyNumber || !this.firstName || !this.lastName || !this.validDate) {
    return false;
  }
  if (this.expirationDate && this.expirationDate <= this.validDate) {
    return false;
  }
  return true;
}

The specification file for the above function might look like the following.

describe("Policy", function() {
  describe("valid", function() {
    var policy;
    beforeEach(function() {
      policy = new Policy();
      policy.policyNumber = "PL98765";
      policy.firstName = "John";
      policy.lastName = "Doe";
      policy.validDate = new Date(2014, 00, 01);
      policy.expirationDate = new Date(2014, 11, 31);
    });
    
    it("should be true when all fields are present", function() {
      expect(policy.valid()).toBe(true);
    });
    
    it("should be false when the policyNumber field is missing", function() {
      policy.policyNumber = null;
      expect(policy.valid()).toBe(false);
    });
    
    it("should be false when the firstName field is missing", function() {
      policy.firstName = null;
      expect(policy.valid()).toBe(false);
    });
    
    it("should be false when the lastName field is missing", function() {
      policy.lastName = null;
      expect(policy.valid()).toBe(false);
    });
    
    it("should be false when the validDate field is missing", function() {
      policy.validDate = null;
      expect(policy.valid()).toBe(false);
    });
    
    it("should be false when the expirationDate field is before validDate", function() {
      policy.expirationDate = new Date(1980, 00, 01);
      expect(policy.valid()).toBe(false);
    });
    
    it("should be false when the expirationDate field is the same as validDate", function() {
      policy.expirationDate = policy.validDate;
      expect(policy.valid()).toBe(false);
    });
  });
});

Running the specifications

To run the specifications (after installing the new package), run the following from the command line:

node_modules/jasmine-node/bin/jasmine-node spec/

To automatically run the specifications upon each change (a la AutoTest), add the –autotest argument:

node_modules/jasmine-node/bin/jasmine-node spec/ --autotest

Running in autotest mode causes Jasmine-Node to automatically rerun the specifications any time a change in one of the specification files is detected. So you can leave a terminal window up and just watch it out of the corner of your eye as you make changes to the source code without needing to explicitly start a test run.

More advanced testing

The only tests added for this part of the series are tests for fairly isolated functions. More of the system can be tested once spies (and other types of mocks) are added. However, these will be covered later in this series.