jqMock is a lightweight javascript Mock Framework for the jqUnit testing framework. jqMock allows functions in dependent functions such as native alert dialogs to be mocked, so that your own code can be tested in isolation.
jqMock has comprehensive expectation support, with complex argument matching criteria, ordered/unordered expectations, multiplicity control, intercepting return values and throwing exceptions. Error messages are clear and explicit, and failures are presented with full details of the satisfaction state.
It is assumed you are already familiar with the jqUnit testing framework.
Download the source for jqMock here:
To include in your tests, add the script to your fixture:
<script type="text/javascript" src="jqMock.js"></script>
It is also suggested you make a call to jqMock.addShortcut() which puts is and expect into the global scope for cleaner code:
jqMock.addShortcut();
jqMock provides an assertion function assertThat which allows you to setup powerful and flexible expectations by providing a comprehensive set of expressions. Even if you do not use this framework for mocking, assertThat is a very useful addition to the jqUnit toolset. For example:
jqMock.assertThat(3,3);
jqMock.assertThat([1,2,3],[1,2,3]);
jqMock.assertThat({a:'a',b:'b'}, {b:'b', a:'a'});
jqMock.assertThat([1,2,3], is.instanceOf(Array));
jqMock.assertThat("foo", is.anyOf(['foo','bar']));
jqMock.assertThat(myobject, is.allOf([is.instanceOf(MyObject),{x:'x'}]));
jqMock comes with the following expressions:
| Expression | Meaning | Example |
|---|---|---|
| is.anything | matches anything |
jqMock.assertThat(foo, is.anything);
|
| is.not() | negate an expression |
jqMock.assertThat(foo, is.not(100));
|
| is.instanceOf() | applies the instanceof operator |
jqMock.assertThat([1,2,3], is.instanceOf(Array));
|
| is.regex() | whether it matches the regex expression provided |
jqMock.assertThat("987", is.regex(/[0-9]*/));
|
| is.anyOf([]) | matches one of the expressions in the array provided |
jqMock.assertThat(foo, is.anyOf(["a", "b", "c"]));
|
| is.allOf([]) | must match all of the expressions in the array provided. Useful for matching multiple not expressions |
jqMock.assertThat(foo, is.allOf([is.not(null), is.not(undefined)]));
|
| is.objectThatIncludes() | matches if the actual object has every field specified in the expected object |
jqMock.assertThat({x:'x', a:'a', y:'y'}, is.objectThatIncludes({a:'a'}));
|
| is.custom(Function) | specify a custom expression as a function that takes in a single argument and returns a boolean value |
function isEven(arg) {
return arg % 2 == 0 ;
};
jqMock.assertThat(foo, is.custom(isEven));
|
| is.exception() | An expression for matching exceptions, to be used with expectThatExceptionThrown(). See the section below for the different ways to create this expression. |
jqMock.expectThatExceptionThrown(function() {
throw new Error("something bad happened");
}, is.exception("something bad happened"));
|
jqMock also provides a method expectThatExceptionThrown to assert that an exception is thrown in a block of code. The following examples shows how loose or strict this assertion can be.
// simple compare
jqMock.expectThatExceptionThrown(function() {
throw "blah";
}, "blah");
// using an expression
jqMock.expectThatExceptionThrown(function() {
throw new Error("booya");
}, is.instanceOf(Error));
/* there is a special expression for exceptions */
// any exception is thrown
jqMock.expectThatExceptionThrown(function() {
throw new Error("anything");
}, is.exception());
// using a string argument will assert ex.message
jqMock.expectThatExceptionThrown(function() {
throw new Error("bad exception 2");
}, is.exception("bad exception 2"));
// you can pass in an object with the attributes name, message and type,
// which asserts ex.name, ex.message, and (ex instanceof type) respectively.
// you can pass in just one or all three attributes.
jqMock.expectThatExceptionThrown(function() {
eval("blah");
}, is.exception({name: "ReferenceError", message: "blah is not defined", type: ReferenceError}));
Let's start with a basic hello world example of how this mock library is useful. Say you wrote this function:
function hello() {
alert("hello world!");
}
You can mock and intercept the call to window.alert, and setup an expectation it is called correctly:
jqUnit.test("hello world test", function() {
var alertMock = new jqMock.Mock(window, "alert");
alertMock.modify().args("hello world!").returnValue();
hello();
alertMock.verify();
alertMock.restore();
});
Lets analyse the above code, step by step.
The args() method will setup the arguments of the expectation. These are basically the arguments you expect your method to be called with. Arguments will match the expectation if they are deeply equal. You can use the expressions seen earlier. You can provide any number of arguments. Here are some examples:
var mock = new jqMock.Mock(myobj, "fn");
mock.modify().args();
mock.modify().args([4,5,6]);
mock.modify().args( {a:'a',b:'b'}, 'c' );
mock.modify().args(is.regex(/^abc[0-9]*xyz$/));
mock.modify().args(is.anyOf(["a", "b", "c"]));
myobj.fn();
myobj.fn( [4,5,6] );
myobj.fn( {a:'a',b:'b'}, 'c' );
myobj.fn( "abc123xyz" )
myobj.fn("a");
mock.verify();
The multiplicity indicates how many times you expect a function to be called with some argument. When no multiplicitiy is specified as in the above examples, it defaults to a multiplicity of exactly one. These are the different ways in which multiplicity can be specified:
| Multiplicity | Meaning | Example |
|---|---|---|
| mulitplicity(n) | An alias for exactly(n) |
mock.modify().args().multiplicity(2);
|
| exactly(n) | Expect that an argument is matched exactly n times |
mock.modify().args().multiplicity(expect.exactly(1));
|
| times(n) | An alias for exactly(n) |
mock.modify().args().multiplicity(expect.times(3));
|
| times(n,m) | range multiplicity. Expect that an argument is matched at least n times, and at most m times. In the example here, the expectation is satisfied if the method is called with no arguments 1,2 or 3 times. |
mock.modify().args().multiplicity(expect.times(1,3));
|
| atLeast(n) | Expect that an argument is matched at least n times. |
mock.modify().args().multiplicity(expect.atLeast(1));
|
| atMost(n) | Expect that an argument is matched at most n times. This argument is initially satisfied, and becomes unsatisfied if it matches more than n times. |
mock.modify().args().multiplicity(expect.atMost(4));
|
The failure report of jqMock uses the same format as the java mock library RMock, and provides a clear description of the failure. Consider the following test. We expect "Bart" to be called twice, "Lisa" once, and "Marge" at least once.
jqUnit.test("failing test", function() {
var mock = new jqMock.Mock(myObj, "sayHello");
mock.modify().args("Bart").multiplicity(2);
mock.modify().args("Lisa");
mock.modify().args("Marge").multiplicity(expect.atLeast(1));
doWork();
mock.verify();
});
In the test above, line 6 calls this code under test:
function MyObject() {};
MyObject.prototype.sayHello = function(person) {
return "hello " + person;
};
var myObj = new MyObject();
function doWork() {
myObj.sayHello("Bart");
myObj.sayHello("Marge");
}
"Bart" has only been called once, and "Lisa" hasn't been called. The results will look like this:

The following diagram shows how to read the report.

When returnValue() is not specified, the function remains intact and you are merely checking expectations. However, you will usually want to intercept and change the return value for easy testing. Calling restore will remove all expectations, and stop intercepting the function.
jqUnit.test("returnValue and intercepting", function() {
var mock = new jqMock.Mock(myObj, "sayHello");
mock.modify().args("Bart"); // use original call
mock.modify().args("Homer").returnValue("doh!"); // intercept returnValue
myObj.sayHello("Bart"); // returns "hello Bart"
myObj.sayHello("Homer"); // returns "doh!"
mock.verify();
mock.restore();
myObj.sayHello("Homer"); // after restore, returns "hello Homer"
});
You can intercept a function and specify something to be thrown as an exception when that function is called.
jqUnit.test("throw exception", function() {
var mock = new jqMock.Mock(myObj, "sayHello");
var myError = new Error("stupid Flanders");
mock.modify().args("Flanders").throwException(myError);
});
Most mock libraries offer the ability to check for unexpected invocations, which fails a test in the event that a method is called with arguments that have not been setup as expectations. However, this is usually too strict, and results in overspecified tests which are hard to maintain.
By default, jqMock.verify() will only check any expectations you have setup. In the following example, the call on "Bart" will be ignored, while the extra call on "Homer" will show up as an error.
jqUnit.test("verify()", function() {
var mock = new jqMock.Mock(myObj, "sayHello");
mock.modify().args("Homer").multiplicity(1);
myObj.sayHello("Bart"); // this is ignored
myObj.sayHello("Homer"); // this will match the expectation
myObj.sayHello("Homer"); // this will fail
mock.verify();
});
The failure report would look like this:

However, if you want to also check for unexpected invocations, use the method jqMock.verifyAll().
If the example code above was changed so that the last line was verifyAll(), then the call with "Bart" will also fail.
jqUnit.test("verifyAll()", function() {
var mock = new jqMock.Mock(myObj, "sayHello");
mock.modify().args("Homer").multiplicity(1);
myObj.sayHello("Bart"); // this call will also fail with verifyAll
myObj.sayHello("Homer");
myObj.sayHello("Homer");
mock.verifyAll(); // change to verifyAll
});
The resulting failure report would look like this:

So far, we have only used undordered expectations. This means expectations can be fullfilled in any order. To use strict ordering, call setOrdered(true) on the mock before setting up any expectations
jqUnit.test("ordered expectations", function() {
var myobj = {echo:function(s){return s;}};
var mock = new jqMock.Mock(myobj, "echo");
mock.setOrdered(true); // use ordered expectations
mock.modify().args(1);
mock.modify().args(2);
mock.modify().args(3);
myobj.echo(1);
myobj.echo(3);
myobj.echo(2);
mock.verifyAll();
});
The results will look like the following. The call to echo(1) was satisfied. However, echo(2) was not the next call, so it stops matching the expectations.

It was suggested that you call addShortcut() so that the expression operator is and the multiplicity operator expect are copied to the global scope. If these conflict with your code, you can just use the original namespaced objects instead:
jqUnit.test("using full namespaced objects", function() {
var mock = new jqMock.Mock(myObj, "sayHello");
mock.modify().args(jqMock.is.anyOf(["Lisa","Bart","Maggie"]));
mock.modify().args("Homer").multiplicity(jqMock.expect.atLeast(1));
doWork();
mock.verify();
});
The following section will discuss some scenarios to explain how more complex expectations are resolved.
In unordered expectations, matching occurs from top down, starting from the first exepctation you setup to the last. For certain types of multiplicity, they will stop matching when it hits upper limit. The following multiplicity will stop matching when the upper limit is reached, so that other expectations can pick it up:
Consider the following test, and the result.
jqUnit.test("upper limit", function() {
var mock = new jqMock.Mock(myObj, "sayHello");
mock.modify().args("Homer").multiplicity(1); // match once
mock.modify().args("Homer").multiplicity(2); // match twice
// function is executed 4 times
myObj.sayHello("Homer");
myObj.sayHello("Homer");
myObj.sayHello("Homer");
myObj.sayHello("Homer");
mock.verify();
});

You can see that the first expectation was matched once, the next expectation was matched twice, and the test failed because there was a call which was unexpected.
The atLeast(n) multiplicity behaves differently, and will ALWAYS match in order to keep the expectation satisfied. Consider this test, and the result.
jqUnit.test("atLeast", function() {
var mock = new jqMock.Mock(myObj, "sayHello");
mock.modify().args("Homer").multiplicity(expect.atLeast(2));
mock.modify().args("Homer").multiplicity(2);
// function is executed 4 times
myObj.sayHello("Homer");
myObj.sayHello("Homer");
myObj.sayHello("Homer");
myObj.sayHello("Homer");
mock.verify();
});

You can see first expectation matched 4 times, while the second expectation did not match at all. If this situation happens to you, the solution is to switch lines 3 and 4, so that the atLeast() expectation doesn't steal all the matches. After the first expectation matches twice, it is disabled so that the second expectation is free to be used.
mock.modify().args("Homer").multiplicity(2);
mock.modify().args("Homer").multiplicity(expect.atLeast(2));