Testing AJAX Module with QUnit

We are trying to implement QUnit JavaScript tests for a JS-heavy web application. We are struggling to find a way to successfully test methods that include jQuery AJAX queries. For example, we have the following design function (obviously, this is a very simplified example):

var X = function() { this.fire = function() { $.ajax("someURL.php", { data: { userId: "james" }, dataType: "json", success: function(data) { //Do stuff } }); }; }; var myX = new X(); myX.fire(); 

We are trying to find a way to test the fire method, preferably with a wired URL instead of the real someURL.php .

The only obvious solution for me at the moment is to add the url and success callback as arguments to the constructor function. Thus, in the test, we can create a new instance of X and pass the stub URL and callback to start when the stub returns a response. For instance:

 test("Test AJAX function", function() { stop(); var myX = new X(); //Call the AJAX function, passing in the stub URL and success callback myX.fire("stub.php", function(data) { console.log(data); start(); }); }); 

However, this does not seem like a very good solution. Is there a better way?

+6
source share
3 answers

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 tags that you define in the options. Therefore, if your async function can return an xhr object, you can add test-specific handlers.

As for the URL, this is a little trickier. I sometimes set up a very simple Node server on localhost, which simply serves up saved responses that were copied from a real server. If you run the test package from the same server, your URLs must be absolute paths to get to the test server instead of the production server. And you also get a record of the requests themselves, as the server sees them. Or you can have a test server to send back errors or bad answers with a goal if you want to see how the code handles it.

But this, of course, is a rather complicated decision. It would be easier to identify your URLs in a place where you can redefine them from a 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 

In addition, QUnit has an asyncTest function that calls stop() for you. Add a tiny helper to keep track of when to start again, and you have a pretty good solution.

Here is what I did 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 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 what you specified, and then calls start() . In this case, one asynchronous call, so countDown will count from this. And this will be done when the ajax call completes, no matter how it happened, as it is configured as an always callback.
And since asyncTest told to expect 2 statements, it will report an error if the .done() never called, since there will be no statements. Therefore, if the call fails completely, you will know it too. If you want to register something on error, you can add the .fail() to the promise chain.

+9
source

If it is a unit test that can (and should) be run in isolation from the server side, you can simply "replace" $.ajax to mimic any behavior. One simple example:

 test("Test AJAX function", function() { // keep the real $.ajax var _real_ajax = $.ajax; // Simulate a successful response $.ajax = function(url, opts) { opts.success({expected: 'response'}); } var myX = new X(); // Call your ajax function myX.fire(); // ... and perform your tests // Don't forgot to restore $.ajax! $.ajax = _real_ajax; }); 

Obviously, you can also make a real ajax call with in-depth url / data:

 // Simulate a successfully response $.ajax = function(url, opts) { opts.success = function(data) { console.log(data); start(); } _real_ajax('stub.php', opts) } 

If you don’t have a difficult answer, I prefer the first approach because it is faster and easier to understand.
However, you can also take a different path and put the Ajax logic in your own method so that you can easily drown it out during the tests.

+3
source

Try using jQuery spy . I created an easier way to use the familiar jQuery syntax to test ajax.

Check this link

+2
source

Source: https://habr.com/ru/post/909275/


All Articles