14

How can I test the onreadystatechange on XMLHttpRequest or pure Javascript AJAX without jQuery? I'm doing this because I'm developing Firefox extension. I guess I have to use spies, but couldn't figure out how because my ajax won't return anything.


    submit : function() {
        var url = window.arguments[0];
        var request = new XMLHttpRequest();
        request.open("POST", 'http://'+this.host+'/doSomething', true);
        request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        request.send("param="+param+"&emotions="+this.getParams());
        request.onreadystatechange = function() {
            if(this.readyState == 4) {
                // alert(this.responseText);
            }
        };

    }

toy
  • 11,711
  • 24
  • 93
  • 176

6 Answers6

39

And what about this one?

beforeEach(function() {
  // spyOn(XMLHttpRequest.prototype, 'open').andCallThrough(); // Jasmine 1.x
  spyOn(XMLHttpRequest.prototype, 'open').and.callThrough(); // Jasmine 2.x
  spyOn(XMLHttpRequest.prototype, 'send');
});

...

it("should call proper YQL! API", function() {
  podcast.load_feed('http://www.faif.us/feeds/cast-ogg/');

  expect(XMLHttpRequest.prototype.open).toHaveBeenCalled();
});

Pure Jasmine without need to use any external library.

Albin
  • 4,180
  • 2
  • 27
  • 19
mcepl
  • 2,688
  • 1
  • 23
  • 38
11

Jasmine has its own Ajax mock library called ajax.js.

taro
  • 699
  • 1
  • 9
  • 20
  • Thanks @taro! I use `jasmine-ajax` and now, my test case looks really simple! Demo: https://github.com/piecioshka/test-angular-services-testing/blob/master/src/app/photos.service.spec.ts#L84 – piecioshka Mar 21 '20 at 19:36
6

As mention in the comments with SinonJS you can easily mock the XHR object / create a fake server.

Andreas Köberle
  • 106,652
  • 57
  • 273
  • 297
5

You can test it in such manner

it("should make XHR request", function() {

   // arrange

    var xhr = {
        open: jasmine.createSpy('open')
    };

    XMLHttpRequest = jasmine.createSpy('XMLHttpRequest');
    XMLHttpRequest.and.callFake(function () {
        return xhr;
    });

    // act

    submit();

    // assert

    expect(xhr.open).toHaveBeenCalled(); 
});
Alvis
  • 3,543
  • 5
  • 36
  • 41
  • It could be a solution to the JavaScript project. In my Angular app TypeScript has complain for overriding XMLHttpRequest which is inferred to XMLHttpRequest interface. – piecioshka Mar 21 '20 at 19:38
1

Provided by jasmine-ajax. To mock for a single spec use withMock:

  it("allows use in a single spec", function() {
    var doneFn = jasmine.createSpy('success');
    jasmine.Ajax.withMock(function() {
      var xhr = new XMLHttpRequest();
      xhr.onreadystatechange = function(args) {
        if (this.readyState == this.DONE) {
          doneFn(this.responseText);
        }
      };

      xhr.open("GET", "/some/cool/url");
      xhr.send();

      expect(doneFn).not.toHaveBeenCalled();

      jasmine.Ajax.requests.mostRecent().respondWith({
        "status": 200,
        "responseText": 'in spec response'
      });

      expect(doneFn).toHaveBeenCalledWith('in spec response');
    });
  });

The response is only sent when you use respondWith. Just download the file, and add as a src to your SpecRunner.html. Example usage at https://github.com/serv-inc/JSGuardian (see the test folder).

serv-inc
  • 35,772
  • 9
  • 166
  • 188
  • jasmine-ajax FTW — demo into my specs: https://github.com/piecioshka/test-angular-services-testing/blob/master/src/app/photos.service.spec.ts#L84 – piecioshka Mar 21 '20 at 19:39
-1

As per the facsimile of your code below you will likely be able to inject console.log() with the status code and status text.

This will help you identify any http errors. Speculatively I'd say the status will return 404.

submit : function() {
    var url = window.arguments[0];
    var request = new XMLHttpRequest();
    request.open("POST", 'http://'+this.host+'/doSomething', true);
    request.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
    request.send("param="+param+"&emotions="+this.getParams());
    request.onreadystatechange = function() {
        console.log(this.status+ " - "+ this.statusText);
    };

}

An alternative to this would be to look at the request header / response within the firebug console. Which is a good point: If you're not using firebug or chrome dev tools you should change the console.log() call to something that appends the string to a document object do not use an alert() call.

How do I verify jQuery AJAX events with Jasmine?. A similar answer that implements Jasmine.

Community
  • 1
  • 1
CBusBus
  • 2,321
  • 1
  • 18
  • 26
  • I'm not familiar with Jasmine however I've added a link to my answer which points to an answer from a similar thread that appears to be the Jasmine test you're looking for. – CBusBus Jan 17 '12 at 21:25