4

I'm struggling to find any examples on how to fake an ajax call using Jasmine BDD?

I have a custom ajax function that works like so...

ajax({
    url: 'JSON.php',
    dataType: 'json',           
    onSuccess: function(resp) {
        console.log(resp);
    }
});

...and I've no idea how to create a stub to fake calling the actual ajax function.

I want to avoid calling the ajax function as it could slow down my test suite if a real ajax call to the server takes some time to respond and I've loads of specs in my test suite.

I've heard that you can use spyOn(namespace, 'ajax') but that is annoying straight away as it requires me to wrap my ajax function in an object just to use the spyOn function (but regardless I wasn't able to follow along as I couldn't find any specific examples to fake an ajax call).

I've also heard that you can use createSpy() but again the documentation isn't very helpful (neither is the corresponding wiki on GitHub).

Any help explaining how to use spies to create a fake ajax call would be greatly appreciated!

serv-inc
  • 35,772
  • 9
  • 166
  • 188
Integralist
  • 2,161
  • 1
  • 32
  • 50

3 Answers3

4

You can use SinonJS mocking framework, which has a build in fake server. You can easily use it with jasmine:

beforeEach(function() {
        server = sinon.fakeServer.create();
        server.respondWith([200, { "Content-Type": "text/html", "Content-Length": 2 }, "OK"])
});

Btw. if your ajax function is in the global namespace why not call spyOn(window, 'ajax')

Andreas Köberle
  • 106,652
  • 57
  • 273
  • 297
  • Thanks for this, I had started working on this myself already yesterday. See Gist: https://gist.github.com/1625810 – Integralist Jan 17 '12 at 09:54
0

Regarding a single function, you may use 'createSpy':

/*var */ajax = createSpy('foo');

var is absent because you want to redefine it, but it is then required that the block where you define this spy is bound to the same scope where real ajax was defined. Or, if you confused with that, use spyOn(window, foo), because you are anyway testing it in the browser.

See this answer for details.

Regarding the ajax call, see Asynchronous Support section in new docs, or better use Clock:

window.ajax = function() {};

var response;

var ajaxSpy = spyOn(window, 'ajax').andCallFake(function(url, callback) {
   setTimeout(function() { callback({ 'foo': 'bar' }); }, 1000);
});

jasmine.Clock.useMock();

function callback(resp) { response = resp;  }

ajax('fake.url', callback);

jasmine.Clock.tick(1500);

expect(ajaxSpy).toHaveBeenCalled();
expect(response).toBeDefined();
expect(response.foo).toBeDefined();
Community
  • 1
  • 1
shaman.sir
  • 3,198
  • 3
  • 28
  • 36
  • An important thing to notice is that `Clock` mock in fact substitues `setTimeout` / `setInterval` calls so they will be called immediately, but in the specified order — this mock is done only to save you from waiting for timeouts – shaman.sir Aug 21 '12 at 13:37
0

If you're ok with not using spies, but instead the add-on jasmine-ajax. To mock for a single spec use withMock:

  it("allows use in a single spec", function() {
    var onSuccess = jasmine.createSpy('success');
    jasmine.Ajax.withMock(function() {
      ajax({
            url: 'JSON.php',
            dataType: 'json',           
            onSuccess: onSuccess
      });
      expect(onSuccess).not.toHaveBeenCalled();

      jasmine.Ajax.requests.mostRecent().respondWith({
        "status": 200,
        "responseText": '{"some": "json"}'
      });

      expect(onSuccess).toHaveBeenCalledWith('{"some": "json"}');
    });
  });

The response is only sent when you use respondWith. The link above has some directions how to install

serv-inc
  • 35,772
  • 9
  • 166
  • 188