Testing JavaScript Using the Jasmine Framework

By Rob Gravelle

I once worked with a development team that eschewed the use of JavaScript because it was difficult to test. Now this was a few years back, but maybe they didn't look very hard for JS testing frameworks. If they had, maybe they would have come across Pivotal Labs' JsUnit Framework. Dating all the way back to 2001, it was the first such framework. As the name suggests, it was quite literally a port of the excellent JUnit for Java. Eventually, JsUnit became Jasmine. Which brings us to the subject of today's article, and that is, how to use Jasmine to test your JavaScript code. In fact, all you really need is a JavaScript enabled browser and the Jasmine script.

Why Jasmine?

Thanks to the proliferation of mobile apps, Jasmine is only one of many players in the JS testing game. Others include qUnit, jQuery's "unofficial official" testing framework, and YUI Test, which is part of Yahoo YUI library. In fact, most JS libraries have their own testing framework. Those might be worth considering if you're partial to a specific JS library. On the other hand, it might be beneficial to have testing classes that are not tied to any particular framework. That's one of the things that I like about Jasmine. Another consideration is whether or not a test framework relies on the browser. Some, like RhinoUnit, are Ant-based and do not require a browser to run, making testing a lot more automated. The choice is yours.

 

Suites and Specs

Although familiarity with JUnit doesn't hurt, setting up tests is easy enough to get the hang of in a few minutes. A Suite represents a bunch of tests that are related. Each suite in turn contains a set of Expectations that compare the results of the test - called the actual - with the expected value. A Suite is defined by calling the describe() function. It takes two parameters: the name of the Suite, and the function which contains the calls to the expectation methods called Specs. These are defined using the it() method. Like describe(), it() also accepts a name and function parameter. The it() function parameter may contain variables and one or more calls to the expect() method. Used in conjunction with a Matcher function, these carry out the task of comparing the actual and expected values. Here's a simple example to demonstrate:

describe("Stock Portfolio App Tests", function() {
  it("calcSideFundInterest() should return a value that is greater than the supplied fund value.", function() {
    var calcSideFundInterest = function(fundValue, dailyInt, period) {
      return fundValue * (dailyInt * period);
    };
    var fundValue = 1000, 
        dailyInt  = 0.00356, 
        period    = 7;
    var result = calcSideFundInterest(fundValue, dailyInt, period);
    expect(result).toBeGreaterThan(fundValue);
  });
});

Pre-defined Matchers

In the above example, the toBeGreaterThan() method is a matcher. It represents a comparison such as ">" using plain English. There are many other matchers available, including:

  • toBe: represents the exact equality (===) operator.
  • toEqual: represents the regular equality (==) operator.
  • toMatch: calls the RegExp match() method behind the scenes to compare string data.
  • toBeDefined: opposite of the JS "undefined" constant.
  • toBeUndefined: tests the actual against "undefined".
  • toBeNull: tests the actual against a null value - useful for certain functions that may return null, like those of regular expressions (same as toBe(null))
  • toBeTruthy: simulates JavaScript boolean casting.
  • toBeFalsy: like toBeTruthy, but tests against anything that evaluates to false, such as empty strings, zero, undefined, etc…
  • toContain: performs a search on an array for the actual value.
  • toBeLessThan/toBeGreaterThan: for numerical comparisons.
  • toBeCloseTo: for floating point comparisons.
  • toThrow: for catching expected exceptions.

The not operator creates a negative assertion placed between the expect and the matcher. ( expect(false).not.toBe(true) )

describe("Stock Portfolio App Tests", function() {
  it("calcWeeklyPercentChange() should return the change between two numbers as a percentage.", function() {
    var calcWeeklyPercentChange = function(presentValue, previousValue, aPercentChanges) {
      var percentChange = presentValue / previousValue - 1;
      aPercentChanges.push(percentChange);
      return percentChange;
    };
    var presentValue    = 110, 
        previousValue   = 100, 
        aPercentChanges = [];
    //actually returns 0.10000000000000009!
    var result = calcWeeklyPercentChange(presentValue, previousValue, aPercentChanges);
    expect(result).toBeCloseTo(0.1);
  });
});

Jasmine also supports the use of your own custom matchers. While that's a little beyond the scope of today's intro article, it's good to know that you aren't limited to the matcher listed above.

Setup and Teardown

Setup and teardown code should be placed in the global beforeEach() and afterEach() methods. These are useful for reinitializing and setting test values between tests. The beforeEach() method is called before each spec (it() method) in the describe and afterEach() is called after each spec.

describe("Stock Portfolio App Tests", function() {
  var presentValue, 
      previousValue, 
      aPercentChanges;

  beforeEach(function() {
    presentValue    = 110; 
    previousValue   = 100; 
    aPercentChanges = [];
  });

  afterEach(function() {
    presentValue    = 0;
    previousValue   = 0; 
    aPercentChanges = [];
  });
  
  it("calcWeeklyPercentChange() should return the change between two numbers as a percentage.", function() {
    var calcWeeklyPercentChange = function(presentValue, previousValue, aPercentChanges) {
      var percentChange = presentValue / previousValue - 1;
      aPercentChanges.push(percentChange);
      return percentChange;
    };

    //actually returns 0.10000000000000009!
    var result = calcWeeklyPercentChange(presentValue, previousValue, aPercentChanges);
    expect(result).toBeCloseTo(0.1);
    expect(aPercentChanges.length).toEqual(1);
  });
  
  it("The aPercentChanges array should now be empty.", function() {
    expect(aPercentChanges.length).toEqual(0);
  });
});

Conclusion

That should be enough to get you going. Download the files from GitHub, read the introduction.js page, and you'll be on your way to writing your own unit tests in no time. In the next article, we'll be taking a look at how to run your tests in the browser, and how to spy on your methods!


If you enjoyed this article, please contribute to Rob's less lucrative music career by purchasing one of Rob's cover or original songs from iTunes.com for only 0.99 cents each.

Rob Gravelle resides in Ottawa, Canada, and is the founder of GravelleConsulting.com. Rob has built systems for Intelligence-related organizations such as Canada Border Services, CSIS as well as for numerous commercial businesses. Email Rob to receive a free estimate on your software project. Should you hire Rob and his firm, you'll receive 15% off for mentioning that you heard about it here!

In his spare time, Rob has become an accomplished guitar player, and has released several CDs. His former band, Ivory Knight, was rated as one Canada's top hard rock and metal groups by Brave Words magazine (issue #92).

Rob uses and recommends MochaHost, which provides Web Hosting at $3.10 per month, 2 LifeTime Free Domains, and 6 Months Free!



Make a Comment

Loading Comments...

  • Web Development Newsletter Signup

    Invalid email
    You have successfuly registered to our newsletter.
  •  
  •  
  •