With jQuery, you can use the xhr object that .ajax()
returns as a promise, so you can add more handlers (see below) than just the single success
, complete
and error
ones you define in the options. So if your async function can return the xhr object, you can add test-specific handlers.
As for the URL, that's a little trickier. I've sometimes set up a very simple Node server on localhost, which just serves canned responses that were copied from the real server. If you run your test suite off that same server, your URLs just need to be absolute paths to hit the test server instead of the production server. And you also get a record of the requests themselves, as a server sees them. Or you can have the test server send back errors or bad responses on purpose, if you want to see how the code handles it.
But that's of course a pretty complex solution. The easier one would be to define your URLs in a place where you can redefine them from the test suite. For instance:
/* in your code */
var X = function () {
this.fire = function () {
return $.ajax({ url: this.constructor.url, ... });
};
};
X.url = "someURL.php"; // the production url
/* in your tests */
X.url = "stub.php"; // redefine to the test url
Also, QUnit has an asyncTest
function, which calls stop()
for you. Add a tiny helper to keep track of when to start again, and you've got a pretty good solution.
Here's what I've done before
// create a function that counts down to `start()`
function createAsyncCounter(count) {
count = count || 1; // count defaults to 1
return function () { --count || start(); };
}
// ....
// an async test that expects 2 assertions
asyncTest("testing something asynchronous", 2, function() {
var countDown = createAsyncCounter(1), // the number of async calls in this test
x = new X;
// A `done` callback is the same as adding a `success` handler
// in the ajax options. It's called after the "real" success handler.
// I'm assuming here, that `fire()` returns the xhr object
x.fire().done(function(data, status, jqXHR) {
ok(data.ok);
equal(data.value, "foobar");
}).always(countDown); // call `countDown` regardless of success/error
});
Basically countDown
is a function that counts down to zero from whatever you specify, and then calls start()
. In this case, there's 1 async call, so countDown
will count down from that. And it'll do so when the ajax call finishes, regardless of how it went, since it's set up as an always
callback.
And because the asyncTest
is told to expect 2 assertions, it'll report an error if the .done()
callback is never called, since no assertions will be run. So if the call completely fails, you'll know that too. If you want to log something on error, you can add a .fail()
callback to the promise chain.
jQuery spy
. I have created an easier way to use jQuery familiar syntax for testing ajax. Check this link – Burnell