Using jqMock to test AJAX calls
Anastasia Cheetham
a.cheetham at utoronto.ca
Thu Jul 30 17:47:36 UTC 2009
Unit testing AJAX calls is troublesome, because an AJAX call is
typically used to communicate with a server, and when unit testing,
you usually don't usually have a server to test against.
jqMock (http://jqmock.googlecode.com/svn/trunk/docs/userguide.html )
is a jqUnit-based testing framework that can help with this. I have
started to use it a little bit in CollectionSpace, and thought I'd
share what I've learned so far, in case it helps anyone.
How can jqMock help with AJAX testing?
--------------------------------------
jqMock can be used to intercept calls to a function that is out of
your control, and verify that you sent it the expected parameters. So
you can use it to intercept calls to jQuery.ajax(), for example, and
at the very least verify that your code constructed the options block
properly. You don't have much control over what the server does with
your request, so really, this is about all you can test anyway.
How do I add jqMock to the testing framework?
---------------------------------------------
jqMock was written to work with the version of jqUnit that is in the
Google repository. The version we have in Infusion has changed, so I
did have to add one extension to our jqUnit to make it work: jqMock
calls jqUnit's "ok()" function, which calls qUnit's "ok()", and which
we removed from jqUnit. When I added it back, all was fine. (NOTE: I
made this mod in CollectionSpace, not in Fluid's trunk.)
The documentation for jqMock suggests calling jqMock.addShortcut(),
but I couldn't get that to work. That simply meant that instead of
using 'is' and 'expect', you have to use 'jqMock.is' and
'jqMock.expect'
How do I set up a test for an ajax call using jqMock?
-----------------------------------------------------
There are 5 basic steps to this process:
1) Create the mock object for the ajax call
2) Set your expectations
3) Execute your tests
4) Verify that the expectations were met
5) Release the ajax interception
1) Create the mock object for the ajax call:
var ajaxMock = new jqMock.Mock(jQuery, "ajax"); // will intercept
calls to the "ajax" function of jQuery
2) Set your expectations:
ajaxMock.modify().args(<object matching what you expect the
arguments to the ajax call to be>);
3) Execute your tests:
Obviously, this depends on your code. In CollectionSpace, I'm
testing the DataContext's update call, which builds up the parameters
to an ajax call, so it looks like this:
testContext.update(modelPath);
4) Verify that the expectations were met:
ajaxMock.verify();
5) Release the ajax interception:
ajaxMock.restore(); // this prevents your mock object from
intercepting any more ajax calls
Notes
-----
1) You can set up multiple expectations on a single mock object,
roughly corresponding to multiple test calls. So in CollectionSpace,
if I want to test my update() function with different model paths, I
might have something like this:
ajaxMock.modify().args(expectedParams_1);
ajaxMock.modify().args(expectedParams_2);
ajaxMock.modify().args(expectedParams_3);
testContest.update(testModelPath_1);
testContest.update(testModelPath_2);
testContest.update(testModelPath_3);
ajaxMock.verify();
Note that there is only one verify() regardless of the number of
expectations. In the results, this will be treated as one test, with
multiple sub-results.
2) Event handlers
I haven't figured out how to verify functions (if you even can), so
the 'success' and 'error' options to the ajax call are troublesome.
For now, you can verify a subset of the fields in an object using
something called "is.objectThatIncludes()" in your expectation. You
can use this to verify the subset of object fields that *can* be
verified, for example:
var expectedParams = {
url: theTestUrlIExpectMyAppToCreate,
type: "GET",
dataType: "json",
data: JSON.stringify(myTestData)
}
ajaxMock.modify().args(jqMock.is.objectThatIncludes(expectedParams));
This will check the four fields I specified against the actual options
block, but ignore the rest.
3) Results display:
- A failure report shows the expected value, but doesn't include the
*actual* wrong value, which makes diagnosing the error harder. I've
filed an issue requesting this enhancement with the project.
- In FF3 on Mac OS X at least, there is a bug in the display of the
failure report that makes it difficult to read, but it's still
possible. I've filed an issue with the project about this.
I hope this all makes some sense. Please, if you do try to use jqMock
and this doesn't make sense, just ask.
--
Anastasia Cheetham a.cheetham at utoronto.ca
Software Designer, Fluid Project http://fluidproject.org
Adaptive Technology Resource Centre / University of Toronto
More information about the fluid-work
mailing list