Smoax: Simple Mock Ajax for jQuery
for Mocha + Chai and Jasmine
- Requires jQuery 1.6+ and either Jasmine 1.0.2+ or some recent Mocha + Chai setup.
- Works with both ye-olde-style ajax success and error handlers and jQuery 1.5 deferreds.
But Why?
Yeah, the world is full of ways to mock jQuery $.ajax.
Most of them do too much. I want jQuery itself to handle response content types, fire globally registered callbacks and such.
So, instead of completely replacing
Get It
Download smoax.js
Mocha + Chai Usage
Add smoax.js to your test runner html:
<script src="smoax.js"></script>
Use it:
var expect = chai.expect
describe('my spec', function() {
beforeEach(function() {
// Initialise smoax and register the matchers
chai.use(smoax.setup())
});
afterEach(function() {
// Revert $.ajax to a real ajax implementation
smoax.release()
})
it('calls some ajax stuff', function() {
// Register expected ajax calls and the responses that they should return
smoax.register('GET', '/my/url.json', { data:'yeah' });
// Call the javascript-under-test that invokes ajax
myProductionCode.someMethod();
// Check that appropriate ajax calls were invoked
expect(smoax).to.have.beenInvokedWith('get', '/my/url.json');
});
});
See the tests for more examples of usage.
Chai Expect Matchers
chai.use(smoax.setup())
registers the following expect matchers:
-
expect(smoax).to.have.beenInvoked()
checks that some ajax got called.
-
expect(smoax).to.have.beenInvokedWith(method, url, data)
checks that an ajax call with the given parameters was called.
-
expect(smoax).latestInvocationToHaveBeen(method, url, data)
checks that the latest ajax call matches the parameters.
Jasmine Usage
Add smoax.js to the Jasmine SpecRunner.html:
<script src="smoax.js"></script>
Use it:
describe('my spec', function() {
beforeEach(function() {
// Initialise smoax and register the matchers
this.addMatchers(smoax.setup());
});
it('calls some ajax stuff', function() {
// Register expected ajax calls and the responses that they should return
smoax.register('GET', '/my/url.json', { data:'yeah' });
// Call the javascript-under-test that invokes ajax
myProductionCode.someMethod();
// Check that appropriate ajax calls were invoked
expect(smoax).toHaveBeenInvokedWith('get', '/my/url.json');
});
});
See the tests for more examples of usage.
Jasmine Matchers
this.addMatchers(smoax.setup())
registers the following Jasmine matchers:
-
expect(smoax).toHaveBeenInvoked()
checks that some ajax got called.
-
expect(smoax).toHaveBeenInvokedWith(method, url, data)
checks that an ajax call with the given parameters was called.
-
expect(smoax).latestInvocationToHaveBeen(method, url, data)
checks that the latest ajax call matches the parameters.
Details, Details
smoax.register(method, url, responseData)
takes three parameters:
- The HTTP method: GET, POST, PUT, etc.
- The URL of the request
- A response body to provide when the the ajax gets invoked. This can be either a string, a javascript object or a function that returns one of the previous types.
smoax.registerError(method, url, statusCode, statusText, responseData)
may be used to test error conditions. It takes the following parameters:
- The HTTP method: GET, POST, PUT, etc.
- The URL of the request
- The HTTP status code to return
- The HTTP status text to return
- A response body to provide when the the ajax gets invoked. This can be either a string, a javascript object or a function that returns one of the previous types.
smoax.register()
and smoax.registerError()
can also be used without the method
and url
parameters, in which case the response is used as a reply to all ajax calls.
smoax.registerDeferred(method, url)
will return a deferred object that allows finer-grained control of when and how the ajax call will return. Resolving the deferred signals an ajax response with a status code of 200, whereas calling .reject()
on the object will return an ajax error:
var deferred = smoax.registerDeferred()
deferred.reject({
statusCode: 400,
statusText: "notfound"
})
Caveat: the deferred object will only respond to a single ajax call when it is resolved or fails. Once the initial ajax call has been started, further deferreds can be registered for subsequent calls.
smoax.calls
contains details of all intercepted ajax calls, a latest
variable pointing to the latest one and a count
variable telling how many ajax calls have been intercepted.
smoax.release()
reverts $.ajax
back to it's original form.
By default, smoax synchronously calls the success and error handlers of an ajax invocation. This is generally good enough, and simplifies test writing by not requiring all test code to become asyncronous. In some special cases, asynchronous behaviour might be nice, and can be enabled with:
smoax.registerAsync(method, url, responseData, timeout)
and smoax.registerAsyncError(method, url, statusCode, statusText, responseData, timeout)
The arguments are the same as for smoax.register
and smoax.registerError
, with an optional delay in milliseconds to wait before sending the response. Defaults to 0.
Also, use smoax.ajax()
instead of $.ajax()
to access a non-mocked version of jQuery.ajax()
during tests to e.g. load example data from files.
Caveats
jQuery automagically serialises GET "data" into the URL before $.ajax gets called, so this won't work expect(smoax).to.have.beenInvokedWith('get', '/url', { key:'value' })
.
Use expect(smoax).to.have.beenInvokedWith('get', '/url?key=value')
instead.
---
Enjoy,