Jasmine - JavaScript Testing

Jasmine is a very handy and powerful framework to write JavaScript unit test. I've been using it to write test cases for a variety of projects. While I was learning how to writing JavaScript unit test code, I found an awesome article about writing JavaScript test with QUnit. I highly recommend this article and appreciate Ben Cherry's work. Then one thought came into my mind. I could use the examples in that article, and try to write some test examples in Jasmine. Hopefully Ben won't get mad with me about this "stealing" activity.

Avoid Singletons

Example from Ben's blog

function newDataStore() {
    var data = [];
    return {
        push : function(item) {
            data.push(item);
        },
        pop : function() {
            return data.pop();
        },
        length : function() {
            return data.length;
        }
    };
}

var dataStore = newDataStore(); 

Jasmine Test Code

describe("A Jasmine example", function() {

    var dataStore;
    beforeEach(function() {
        dataStore = new newDataStore();
    });

    afterEach(function() {
        dataStore = null;
    });

    it("returns the most-recently pushed item", function() {
        dataStore.push("foo");
        dataStore.push("bar")
        expect(dataStore.pop()).toBe("bar");
    });

    it("adds 1 item makes the length 1", function() {
        dataStore.push("foo");
        expect(dataStore.length()).toBe(1);
    });
});

Avoid Closure-based Privacy

Example from Ben's blog

function Templater() {
    this._templates = {};
}

Templater.prototype = {
    _supplant : function(str, params) {
        for (var prop in params) {
            str.split("{" + prop + "}").join(params[prop]);
        }
        return str;
    },
    render : function(name, params) {
        if ( typeof this._templates[name] !== "string") {
            throw "Template " + name + " not found!";
        }

        return this._supplant(this._templates[name], params);
    },
    defineTemplate : function(name, template) {
        this._templates[name] = template;
    }
};

Jasmine Test Code

describe("A Jasmine example", function() {

    var templater;
    beforeEach(function() {
        var templater = new Templater();
    });

    afterEach(function() {
        templater = null;
    });

    it("tests _supplant ", function() {
        expect(templater._supplant("{foo}", {
            foo : "bar"
        })).toBe("{foo}");
        expect(templater._supplant("foo {bar}", {
            bar : "baz"
        })).toBe("foo {bar}");
    });

    it("tests defineTemplate ", function() {
        templater.defineTemplate("foo", "{foo}");
        expect(templater._templates.foo).toBe("{foo}");
    });

    it("tests render", function() {
        templater.defineTemplate("hello", "hello {world}!");
        expect(templater.render("hello", {
            world : "internet"
        })).toBe("hello {world}!");
    });

});

Write Tight Functions

Example from Ben's blog

function _getRedirectPart(url) {
    if (url.charAt(0) === "#") {
        return "hash";
    } else if (url.charAt(0) === "/") {
        return "pathname";
    } else {
        return "href";
    }
}

function redirectTo(url) {
    window.location[_getRedirectPart(url)] = url;
}

Jasmine Test Code

it("tests a URL rewriter _getRedirectPart", function() {
    expect(_getRedirectPart("#foo")).toBe("hash");
    expect(_getRedirectPart("/foo")).toBe("pathname");
    expect(_getRedirectPart("http://foo.com")).toBe("href");
});

Write Lots of Tests

Example from Ben's blog

function testCases(fn, context, tests) {
    for (var i = 0; i < tests.length; i++) {
        same(fn.apply(context, tests[i][0]), tests[i][1],
            tests[i][2] || JSON.stringify(tests[i]));
    }
}

Jasmine Test Code

it("tests foo", function() {
    testCases(foo, null, [
        [["bar", "baz"], "barbaz"],
        [["bar", "bar"], "barbar", "a passing test"]
    ]);
});

Jasmine also supports Asynchronous Test

This feature is extremely important for certain projects, especially when doing the functional test. Example:

describe("A Jasmine Asynchronous Test example", function() {

    var foo, value;
    beforeEach(function() {
        foo = { bar: { baz: "qux"} };
        value = 0;
    });

    afterEach(function() {
        foo = null;
        value = null;
    });

    it("supports async test", function() {
        run(function() {
            var flag = false;
            window.addEventListener("message", function(event) {
        if(event.data) {
            value++;
            flag = true;
        }
            }, false);
            window.postMessage(foo, "geniuscarrier.com");
        });

        waitsFor(function() {
            return flag;
        }, "The value should be true", 1000);

        run(function() {
           expect(value).toBeGreaterThan(0);
        });
    });
});

Yang Zhao

Read more posts by this author.


Comment