Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Stubbing jQuery.ajax in node environment (jQuery 2.x)

I am trying to run some tests that require stubbing jQuery.ajax. I'm using SinonJS to do that and it used to work fine with older version of jQuery (1.x)

var $ = require('jquery');
var sinon = require("sinon");
sinon.stub($, "ajax"); // this worked because $.ajax is defined

However, after upgrading to jQuery 2.x, I have had to include a window environment when I require jquery from my module for it to run. I am using jsdom to accomplish this:

var document = require('jsdom').jsdom(),
    window  = document.parentWindow,
    $       = require('jquery')(window);

PROBLEM $.ajax is now undefined. I suspect because now it returns the jQuery object bound to a specific element but not entirely sure. Does anyone know why and how to get around this?

EDIT A buddy of mine who isn't on SO has pointed out that if we attach window to global, we can get the plain jquery object instead of the factory

    global.window = require('jsdom').jsdom().parentWindow;
    var $ = require('jquery'); // this works as $.ajax is now defined

I'm not a fan of attaching window to global as it will affect up some of the plugins which type check window. Not a blocker, but I'd love to see if there is any other way to go around this problem.

like image 270
Lim H. Avatar asked Apr 29 '15 13:04

Lim H.


People also ask

What is JQuery and Ajax?

JQuery is a JavaScript library, a framework that helps you use JavaScript to simplify common web tasks; Ajax is a technique using JavaScript to construct an XMLHttpRequest.

What is Ajax in node JS?

AJAX: AJAX stands for asynchronous javascript and XML the user to make a request to the server for the data without any reloading and without block, any other request also so provide a smooth performance to fetch the data to the server and show to the page.

How does Ajax return an API call?

The A in Ajax stands for asynchronous. That means sending the request (or rather receiving the response) is taken out of the normal execution flow. In your example, $. ajax returns immediately and the next statement, return result; , is executed before the function you passed as success callback was even called.


2 Answers

I could have sworn that after reading jquery source, I tried this on the day I asked the question but it didn't work. I tried again just now and it's working.

tl;dr jQuery attaches $ to the window namespace for browser emulator.

var document    = require('jsdom').jsdom(),
    window      = document.parentWindow;
require('jquery')(window);
var $ = window.$;

Hopefully it's useful to someone else.

like image 110
Lim H. Avatar answered Oct 25 '22 00:10

Lim H.


While Stubs are nice, they are not as good as Fakes which are not as good as Mocks. I would advise using the more intriguing features of Sinon to create Fakes.

Rather than stubbing the window.$, you can fake the XMLHttpRequest and or XMLHttpResponse

var xhr, requests;

before(function () {
    xhr = sinon.useFakeXMLHttpRequest();
    requests = [];
    xhr.onCreate = function (req) { requests.push(req); };
});

after(function () {
    // Like before we must clean up when tampering with globals.
    xhr.restore();
});

it("makes a GET request for todo items", function () {
    getTodos(42, sinon.spy());

    assert.equals(requests.length, 1);
    assert.match(requests[0].url, "/todo/42/items");
});

Or you can even mock a server

var server;

before(function () { server = sinon.fakeServer.create(); });
after(function () { server.restore(); });

it("calls callback with deserialized data", function () {
    var callback = sinon.spy();
    getTodos(42, callback);

    // This is part of the FakeXMLHttpRequest API
    server.requests[0].respond(
        200,
        { "Content-Type": "application/json" },
        JSON.stringify([{ id: 1, text: "Provide examples", done: true }])
    );

    assert(callback.calledOnce);
});

You can get very creative, Mocking timeouts, delays, 404's, 401's. Because you will still be using the JQuery.Ajax object library, while injecting spies that augment requests and responses, you can create more authentic and robust tests with less effort than if you had to stub all possibilities.

like image 24
Dave Alperovich Avatar answered Oct 24 '22 23:10

Dave Alperovich