最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Stubbing jQuery.ajax in node environment (jQuery 2.x) - Stack Overflow

programmeradmin2浏览0评论

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.

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.

Share Improve this question edited May 8, 2015 at 13:56 Louis 151k28 gold badges284 silver badges328 bronze badges asked Apr 29, 2015 at 13:20 Lim H.Lim H. 10.1k9 gold badges50 silver badges75 bronze badges 6
  • JSDom works with io.js and not "deprecated" environments (like node) so maybe an update of your backend will solve it. – Benjamin Gruenbaum Commented Apr 29, 2015 at 13:23
  • @BenjaminGruenbaum I'd love to be I have to stick with node for now. I pin jsdom to 3.x so that shouldn't have been a problem. – Lim H. Commented Apr 29, 2015 at 13:24
  • Did you see stackoverflow.com/a/8916217/1348195 ? – Benjamin Gruenbaum Commented Apr 29, 2015 at 13:25
  • @BenjaminGruenbaum I did. The question has $.ajax defined. It's just not working. I don't have this function bound to my jQuery object. – Lim H. Commented Apr 29, 2015 at 13:27
  • Why did you want to use jquery ajax ? Request is easier. – user2226755 Commented May 7, 2015 at 15:27
 |  Show 1 more comment

3 Answers 3

Reset to default 7

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.

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.

You have a couple of choices to do requests in node:

1) With jquery

var $ = require('jquery')(require("jsdom").jsdom().parentWindow);
// now $.ajax works well

2) With npm request https://www.npmjs.com/package/request

3) Using native object XHR

I test these three options and finally I used package request (to avoid extra packages like jquery + jsdom) and for strange cases native XHR.

发布评论

评论列表(0)

  1. 暂无评论