Jasmine

javascript-testing

Important:
http://net.tutsplus.com/tutorials/javascript-ajax/testing-your-javascript-with-jasmine/ - done reading
https://github.com/pivotal/jasmine/releases - This is the download page
https://jasmine.github.io/2.5/introduction - done reading (can also do ajax and other things)
https://github.com/velesin/jasmine-jquery
https://github.com/searls/jasmine-fixture

https://github.com/jasmine/jasmine/wiki
http://pivotal.github.com/jasmine/
http://pivotal.github.io/jasmine/
http://skillsmatter.com/podcast/ajax-ria/javascript-tdd-workshop-using-jasmine-to-test-drive-your-javascript-application
https://github.com/velesin/jasmine-jquery
http://techoctave.com/c7/posts/77-javascript-testing-with-jasmine
http://code.tutsplus.com/tutorials/build-a-complete-mvc-website-with-expressjs--net-34168 - done reading
https://github.com/tutsplus/build-complete-website-expressjs - done reading
http://khaidoan.wikidot.com/expressjs-complete-mvc - done reading

// JavaScript - Unit Testing with Jasmine:

describe('JavaScript addition operator', function () {
  it('adds two numbers together', function () {
    expect(1 + 2).toEqual(3);
  });
});

In the above code, the first line start with 'describe', and the second line
start with 'it'.  Both the describe and it functions take two parameters: a 
text string and a function. Most test frameworks try to read as much like 
English as possible, and you can see this with Jasmine. First, notice that the 
string passed to describe and the string passed to the 'it' function.

Inside that it block, you can write all the setup code you need for your test. 
Once you’re ready to write the actual test code, you’ll start with the expect 
function, passing it whatever you are testing.  Notice how this forms a sentence 
as well: we “expect 1 + 2 to equal 3.”

Whatever value you pass into expect will be tested. The method you call, off the 
returned value of expect, will be determined by which test is run. This group of 
methods is called ‘matchers’.

In this case, we’re using the toEqual matcher, which checks to see that the 
value passed to expect and the value passed to toEqual are the same value.

# Global installation
npm install -g jasmine
jasmine init // initialize a project for Jasmine
jasmine examples // seed your project with some examples
jasmine // run your test suite

After executing the above commands, we will find the lib and spec folders in our
current working directory.

Customize spec/support/jasmine.json to enumerate the source and spec files you 
would like the Jasmine runner to include. You may use dir glob strings.  
Alternatively, you may specify the path to your jasmine.json by setting an 
environment variable:

jasmine JASMINE_CONFIG_PATH=relative/path/to/your/jasmine.json

Head on over to the standalone download page and get the latest version.  Extract
the downloaded file, and you should find the lib folder, the spec folder, the 
src folder, and the SpecRunner.html file.

You’ll find the actual Jasmine framework files in the lib folder.  Now, if we
open the SpecRunner.html file using Firefox, it displays:

    Player
        should be able to play a Song
        when song has been paused
            should indicate that the song is currently paused
            should be possible to resume
        tells the current song if the user has made it a favorite
        #resume
            should throw an exception if song is already playing

The “actual” JavaScript ( the code we want to test) can be found in the src 
subdirectory; we’ll be putting ours there shortly. The testing 
code—the specs—go in the spec folder.

We should look at the code inside spec/PlayerSpec.js.

describe( "Convert library", function () {
  describe( "distance converter", function () {
    it("converts inches to centimeters", function () {
      expect(Convert(12, "in").to("cm")).toEqual(30.48);
    });

    it("converts centimeters to yards", function () {
      expect(Convert(2000, "cm").to("yards")).toEqual(21.87);
    });

    it("throws an error when passed an unknown from-unit", function () {
      var testFn = function () {
        Convert(1, "dollar").to("yens");
      }
      expect(testFn).toThrow(new Error("unrecognized from-unit"));
    });

    it("throws an error when passed an unknown to-unit", function () {
      var testFn = function () {
        Convert(1, "cm").to("furlongs");
      }
      expect(testFn).toThrow(new Error("unrecognized to-unit"));
    });

  });

  describe( "volume converter", function () {
  });
});

Notice that we’re nesting describe statements here. This is perfectly legal. 
It’s actually a great way to test seperate functionality chunks of the same 
codebase. Instead of two seperate describe calls for Convert library’s distance 
conversions and volume conversions, we can have a more descriptive suite of 
tests like this.

In the above code, we only added tests for the "distance converter" module.  
We omitted the tests for the "volume converter" module for clarity.  The above
code use the expect method and the toEqual method.  The above code also show
us how to test for exceptions, using the toThrow method.

In the above code, we are wrapping the actual conversion in a function and 
passing that to the expect function. That’s because we can’t call the function 
as the expect parameter; we need to hand it a function and let it call the 
function itself. Since we need to pass a parameter to that to function, we can 
do it this way.

If we try to run the test runner before implementing the system under test, we
will see "ReferenceError".  In other words, our tests will fail until we 
write the actual code.

// toBeDefined()
it("is defined", function () {
  var name = "Andrew";
  expect(name).toBeDefined();
})

// toBeUndefined()
it("is not defined", function () {
  var name;
  expect(name).toBeUndefined();
});

// toBeTruthy
it("is true", function () {
  expect(Lib.isAWeekDay()).toBeTruthy();
});

// toBeFalsy
it("is false", function () {
  expect(Lib.finishedQuiz).toBeFalsy();
});

// toBeLessThan
it("is less than 10", function () {
  expect(5).toBeLessThan(10);
});

// toBeGreaterThan
it("is greater than 10", function () {
  expect(20).toBeGreaterThan(10);
});

// toMatch
it("outputs the right text", function () {
  expect(cart.total()).toMatch(/\$\d*.\d\d/);
});

// toContain
it("should contain oranges", function () {
  expect(["apples", "oranges", "pears"]).toContain("oranges");
});

https://github.com/pivotal/jasmine/wiki/Matchers

If you want a matcher that doesn’t exist, you should be able to do just about 
anything with some set-up code and the matchers Jasmine provides, but sometimes 
it’s nicer to abstract some of that logic to have a more readable test.  Jasmine 
allows us to create our own matchers. But to do this, we’ll need to learn a 
little something else first.

To have code that run before each test, pass your own function to the
beforeEach method:

beforeEach(function () {
  ...
});

To have code that run after each test, pass your own function to the afterEach
method:

afterEach(function () {
  obj.setState("clean");
});

We can add a matcher in either a BeforeEach call or an it call:

beforeEach(function () {
  this.addMatchers({

  });
});

We call this.addMatchers, passing it an object parameter. Every key in this 
object will become a matcher’s name, and the associated function (the value) 
will be how it is run. Let’s say we want to create a matcher that with check to 
see if one number is between two others:

beforeEach(function () {
  this.addMatchers({
    toBeBetween: function (rangeFloor, rangeCeiling) {
      if (rangeFloor > rangeCeiling) {
        var temp = rangeFloor;
        rangeFloor = rangeCeiling;
        rangeCeiling = temp;
      }
      return this.actual > rangeFloor && this.actual < rangeCeiling;
    }
  });
});

We simply take two parameters, make sure the first one is smaller than the 
second, and return a boolean statement that evaluates to true if our conditions 
are met. The important thing to notice here is how we get a hold of the value 
that was passed to the expect function: this.actual. 

The SpecHelper.js file has a beforeEach call that adds the matcher tobePlaying().

There’s a lot more you can do with Jasmine: function-related matchers, spies, 
asynchronous specs, and more.

beforeAll
afterAll

Another way to share variables between a beforeEach, it, and afterEach is 
through the this keyword. Each spec's beforeEach/it/afterEach has the this as 
the same empty object that is set back to empty for the next spec's beforeEach /
it / afterEach.

Suites can be disabled with the xdescribe function. These suites and any specs 
inside them are skipped when run and thus their results will not appear in the 
results.

Pending specs do not run, but their names will show up in the results as pending.
Any spec declared with xit is marked as pending.  Any spec declared without a 
function body will also be marked pending in results.  And if you call the 
function pending anywhere in the spec body, no matter the expectations, the spec 
will be marked pending. A string passed to pending will be treated as a reason 
and displayed when the suite finishes.

describe("Player", function() {
  var player;
  var song;

  beforeEach(function() {
    player = new Player();
    song = new Song();
  });

  it("should be able to play a Song", function() {
    player.play(song);
    expect(player.currentlyPlayingSong).toEqual(song);

    //demonstrates use of custom matcher
    expect(player).toBePlaying(song);
  });

  describe("when song has been paused", function() {
    beforeEach(function() {
      player.play(song);
      player.pause();
    });

    it("should indicate that the song is currently paused", function() {
      expect(player.isPlaying).toBeFalsy();

      // demonstrates use of 'not' with a custom matcher
      expect(player).not.toBePlaying(song);
    });

    it("should be possible to resume", function() {
      player.resume();
      expect(player.isPlaying).toBeTruthy();
      expect(player.currentlyPlayingSong).toEqual(song);
    });
  });

  // demonstrates use of spies to intercept and test method calls
  it("tells the current song if the user has made it a favorite", function() {
    spyOn(song, 'persistFavoriteStatus');

    player.play(song);
    player.makeFavorite();

    expect(song.persistFavoriteStatus).toHaveBeenCalledWith(true);
  });

  //demonstrates use of expected exceptions
  describe("#resume", function() {
    it("should throw an exception if song is already playing", function() {
      player.play(song);

      expect(function() {
        player.resume();
      }).toThrowError("song is already playing");
    });
  });
});

Jasmine has test double functions called spies. A spy can stub any function and 
tracks calls to it and all arguments. A spy only exists in the describe or it 
block in which it is defined, and will be removed after each spec. There are 
special matchers for interacting with spies.

The toHaveBeenCalled matcher will return true if the spy was called.  The 
toHaveBeenCalledTimes matcher will pass if the spy was called the specified 
number of times.  The toHaveBeenCalledWith matcher will return true if the 
argument list matches any of the recorded calls to the spy.

By chaining the spy with and.callThrough, the spy will still track all calls to 
it but in addition it will delegate to the actual implementation.

By chaining the spy with and.returnValue, all calls to the function will return 
a specific value.

By chaining the spy with and.returnValues, all calls to the function will return 
specific values in order until it reaches the end of the return values list, at 
which point it will return undefined for all subsequent calls.

By chaining the spy with and.callFake, all calls to the spy will delegate to the 
supplied function.

If the function being spied on receives arguments that the fake needs, you can 
get those as well.

By chaining the spy with and.throwError, all calls to the spy will throw the 
specified value as an error.

When a calling strategy is used for a spy, the original stubbing behavior can be 
returned at any time with and.stub.

Every call to a spy is tracked and exposed on the calls property.  .calls.any(): 
returns false if the spy has not been called at all, and then true once at least 
one call happens.

.calls.count(): returns the number of times the spy was called.

.calls.argsFor(index): returns the arguments passed to call number index.

.calls.allArgs(): returns the arguments to all calls.

.calls.all(): returns the context (the this) and arguments passed all calls.

.calls.mostRecent(): returns the context (the this) and arguments for the most 
recent call.  

.calls.first(): returns the context (the this) and arguments for the first call.

When inspecting the return from all(), mostRecent() and first(), the object 
property is set to the value of this when the spy was called.

.calls.reset(): clears all tracking for a spy.

When there is not a function to spy on, jasmine.createSpy can create a "bare" 
spy. This spy acts as any other spy - tracking calls, arguments, etc. But there 
is no implementation behind it. Spies are JavaScript objects and can be used as 
such.

In order to create a mock with multiple spies, use jasmine.createSpyObj and pass 
an array of strings. It returns an object that has a property for each string 
that is a spy.

jasmine.any takes a constructor or "class" name as an expected value. It returns 
true if the constructor matches the constructor of the actual value.

jasmine.anything returns true if the actual value is not null or undefined.

jasmine.objectContaining is for those times when an expectation only cares about 
certain key/value pairs in the actual.

jasmine.arrayContaining is for those times when an expectation only cares about 
some of the values in an array.

jasmine.stringMatching is for when you don't want to match a string in a larger 
object exactly, or match a portion of a string in a spy expectation.

When you need to check that something meets a certain criteria, without being 
strictly equal, you can also specify a custom asymmetric equality tester simply 
by providing an object that has an asymmetricMatch function.

The Jasmine Clock is available for testing time dependent code.  It is installed 
with a call to jasmine.clock().install in a spec or suite that needs to 
manipulate time.

Be sure to uninstall the clock after you are done to restore the original 
functions.

You can make setTimeout or setInterval synchronous executing the registered 
functions only once the clock is ticked forward in time.  To execute registered 
functions, move time forward via the jasmine.clock().tick function, which takes 
a number of milliseconds.

The Jasmine Clock can also be used to mock the current date.  If you do not 
provide a base time to mockDate it will use the current date.

Jasmine also has support for running specs that require testing asynchronous 
operations.  Calls to beforeAll, afterAll, beforeEach, afterEach, and it can 
take an optional single argument that should be called when the async work is 
complete.

This spec will not start until the done function is called in the call to 
beforeEach above. And this spec will not complete until its done is called.

By default jasmine will wait for 5 seconds for an asynchronous spec to finish 
before causing a timeout failure. If the timeout expires before done is called, 
the current spec will be marked as failed and suite execution will continue as 
if done was called.

If specific specs should fail faster or need more time this can be adjusted by 
passing a timeout value to it, etc.  If the entire suite should have a different 
timeout, jasmine.DEFAULT_TIMEOUT_INTERVAL can be set globally, outside of any 
given describe.

The done.fail function fails the spec and indicates that it has completed.

    A suite
        contains spec with an expectation

    A suite is just a function
        and so is a spec

    The 'toBe' matcher compares with ===
        and has a positive case
        and can have a negative case

    Included matchers:
        The 'toBe' matcher compares with ===
        The 'toEqual' matcher
            works for simple literals and variables
            should work for objects
        The 'toMatch' matcher is for regular expressions
        The 'toBeDefined' matcher compares against `undefined`
        The `toBeUndefined` matcher compares against `undefined`
        The 'toBeNull' matcher compares against null
        The 'toBeTruthy' matcher is for boolean casting testing
        The 'toBeFalsy' matcher is for boolean casting testing
        The 'toContain' matcher
            works for finding an item in an Array
            also works for finding a substring
        The 'toBeLessThan' matcher is for mathematical comparisons
        The 'toBeGreaterThan' matcher is for mathematical comparisons
        The 'toBeCloseTo' matcher is for precision math comparison
        The 'toThrow' matcher is for testing if a function throws an exception
        The 'toThrowError' matcher is for testing a specific thrown exception

    A spec using the fail function
        SPEC HAS NO EXPECTATIONS should not call the callBack

    A spec
        is just a function, so it can contain any code
        can have more than one expectation

    A spec using beforeEach and afterEach
        is just a function, so it can contain any code
        can have more than one expectation

    A spec using beforeAll and afterAll
        sets the initial value of foo before specs run
        does not reset foo between specs

    A spec
        can use the `this` to share state
        prevents test pollution by having an empty `this` created for the next spec

    A spec
        is just a function, so it can contain any code
        can have more than one expectation
        nested inside a second describe
            can reference both scopes as needed

    A spec
        is just a function, so it can contain any code

    Pending specs
        can be declared 'xit' PENDING WITH MESSAGE: Temporarily disabled with xit
        can be declared with 'it' but without a function
        can be declared by calling 'pending' in the spec body PENDING WITH MESSAGE: this is why it is pending

    A spy
        tracks that the spy was called
        tracks that the spy was called x times
        tracks all the arguments of its calls
        stops all execution on a function

    A spy, when configured to call through
        tracks that the spy was called
        should not affect other functions
        when called returns the requested value

    A spy, when configured to fake a return value
        tracks that the spy was called
        should not affect other functions
        when called returns the requested value

    A spy, when configured to fake a series of return values
        tracks that the spy was called
        should not affect other functions
        when called multiple times returns the requested values in order

    A spy, when configured with an alternate implementation
        tracks that the spy was called
        should not affect other functions
        when called returns the requested value

    A spy, when configured to throw an error
        throws the value

    A spy
        can call through and then stub in the same spec

    A spy
        tracks if it was called at all
        tracks the number of times it was called
        tracks the arguments of each call
        tracks the arguments of all calls
        can provide the context and arguments to all calls
        has a shortcut to the most recent call
        has a shortcut to the first call
        tracks the context
        can be reset

    A spy, when created manually
        is named, which helps in error reporting
        tracks that the spy was called
        tracks its number of calls
        tracks all the arguments of its calls
        allows access to the most recent call

    Multiple spies, when created manually
        creates spies for each requested function
        tracks that the spies were called
        tracks all the arguments of its calls

    jasmine.any
        matches any value
        when used with a spy
            is useful for comparing arguments

    jasmine.anything
        matches anything
        when used with a spy
            is useful when the argument can be ignored

    jasmine.objectContaining
        matches objects with the expect key/value pairs
        when used with a spy
            is useful for comparing arguments

    jasmine.arrayContaining
        matches arrays with some of the values
        when used with a spy
            is useful when comparing arguments

    jasmine.stringMatching
        matches as a regexp
        when used with a spy
            is useful for comparing arguments

    custom asymmetry
        dives in deep
        when used with a spy
            is useful for comparing arguments

    Manually ticking the Jasmine Clock
        causes a timeout to be called synchronously
        causes an interval to be called synchronously
        Mocking the Date object
            mocks the Date object and sets it to a given time

    Asynchronous specs
        should support async execution of test preparation and expectations
        long asynchronous specs
            SPEC HAS NO EXPECTATIONS takes a long time
        A spec using done.fail
            SPEC HAS NO EXPECTATIONS should not call the second callBack

How can we install jasmine using npm?

npm install -g jasmine-node

How can we write tests for our application?

Let's create a tests directory which will hold our tests. The first thing that we are going to check is our configuration setup. The spec files must end with .spec.js, so the file should be called config.spec.js.

describe("Configuration setup", function() {
    it("should load local configurations", function(next) {
        var config = require('../config')();
        expect(config.mode).toBe('local        next();
    });
    it("should load staging configurations", function(next) {
        var config = require('../config')('staging');
        expect(config.mode).toBe('staging        next();
    });
    it("should load production configurations", function(next) {
        var config = require('../config')('production');
        expect(config.mode).toBe('production        next();
    });
});

How can we run our tests?

jasmine-node ./tests

How can we test if a mongodb instance is running?

We are going to write a test, which checks if there is a mongodb server running. /tests/mongodb.spec.js file:

describe("MongoDB", function() {
    it("is there a server running", function(next) {
        var MongoClient = require('mongodb').MongoClient;
        MongoClient.connect('mongodb://127.0.0.1:27017/fastdelivery', function(err, db) {
            expect(err).toBe(null);
            next();
        });
    });
});

How can we test all of our models?

The model is what will be handling the data that's in our application. It should have access to a db object, returned by MongoClient. Our model should also have a method for extending it, because we may want to create different types of models. For example, we might want a BlogModel or a ContactsModel. So we need to write a new spec: /tests/base.model.spec.js, in order to test these two model features. And remember, by defining these functionalities before we start coding the implementation, we can guarantee that our module will do only what we want it to do.

var Model = require("../models/Base"),
    dbMockup = {};
describe("Models", function() {
    it("should create a new model", function(next) {
        var model = new Model(dbMockup);
        expect(model.db).toBeDefined();
        expect(model.extend).toBeDefined();
        next();
    });
    it("should be extendable", function(next) {
        var model = new Model(dbMockup);
        var OtherTypeOfModel = model.extend({
            myCustomModelMethod: function() { }
        });
        var model2 = new OtherTypeOfModel(dbMockup);
        expect(model2.db).toBeDefined();
        expect(model2.myCustomModelMethod).toBeDefined();
        next();
    })
});

Instead of a real db object, I decided to pass a mockup object. That's because later, I may want to test something specific, which depends on information coming from the database. It will be much easier to define this data manually.

The implementation of the extend method is a little bit tricky, because we have to change the prototype of module.exports, but still keep the original constructor. Thankfully, we have a nice test already written, which proves that our code works. A version which passes the above, looks like this:

module.exports = function(db) {
    this.db = db;
};
module.exports.prototype = {
    extend: function(properties) {
        var Child = module.exports;
        Child.prototype = module.exports.prototype;
        for(var key in properties) {
            Child.prototype[key] = properties[key];
        }
        return Child;
    },
    setDB: function(db) {
        this.db = db;
    },
    collection: function() {
        if(this._collection) return this._collection;
        return this._collection = this.db.collection('fastdelivery-content    }
}

Here, there are two helper methods. A setter for the db object and a getter for our database collection.

How can we test our views?

Here is the spec for the /views/Base.js:

var View = require("../views/Base");
describe("Base view", function() {
    it("create and render new view", function(next) {
        var responseMockup = {
            render: function(template, data) {
                expect(data.myProperty).toBe('value');
                expect(template).toBe('template-file');
                next();
            }
        }
        var v = new View(responseMockup, 'template-file');
        v.render({myProperty: 'value'});
    });
    it("should be extendable", function(next) {
        var v = new View();
        var OtherView = v.extend({
            render: function(data) {
                expect(data.prop).toBe('yes                next();
            }
        });
        var otherViewInstance = new OtherView();
        expect(otherViewInstance.render).toBeDefined();
        otherViewInstance.render({prop: 'yes'});
    });
});

In order to test the rendering, I had to create a mockup. In this case, I created an object which imitates the Express's response object. In the second part of the test, I created another View class which inherits the base one and applies a custom render method. Here is the /views/Base.js class.

module.exports = function(response, template) {
    this.response = response;
    this.template = template;
};
module.exports.prototype = {
    extend: function(properties) {
        var Child = module.exports;
        Child.prototype = module.exports.prototype;
        for(var key in properties) {
            Child.prototype[key] = properties[key];
        }
        return Child;
    },
    render: function(data) {
        if(this.response && this.template) {
            this.response.render(this.template, data);
        }
    }
}

How can we test our base controller?

The express(1) command line tool creates a directory named routes, but in our case, it is better for it to be named controllers, so I changed it to reflect this naming scheme.

Since we're not just building a teeny tiny application, it would be wise if we created a base class, which we can extend. If we ever need to pass some kind of functionality to all of our controllers, this base class would be the perfect place. Again, I'll write the test first, so let's define what we need:

  • it should have anextend method, which accepts an object and returns a new child instance
  • the child instance should have a run method, which is the old middleware function
  • there should be a name property, which identifies the controller
  • we should be able to create independent objects, based on the class

The test would look something like this:

var BaseController = require("../controllers/Base");
describe("Base controller", function() {
    it("should have a method extend which returns a child instance", function(next) {
        expect(BaseController.extend).toBeDefined();
        var child = BaseController.extend({ name: "my child controller" });
        expect(child.run).toBeDefined();
        expect(child.name).toBe("myd controller");
        next();
    });
    it("should be able to create different childs", function(next) {
        var childA = BaseController.extend({ name: "child A", customProperty: 'value' });
        var childB = BaseController.extend({ name: "child B" });
        expect(childA.name).not.toBe(childB.name);
        expect(childB.customProperty).not.toBeDefined();
        next();
    });
});

And here is the implementation of /controllers/Base.js:

var _ = require("underscore");
module.exports = {
    name: "base",
    extend: function(child) {
        return _.extend({}, this, child);
    },
    run: function(req, res, next) {

    }
}

Of course, every child class should define its own run method, along with its own logic.

Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License