【问题标题】:Mocking jQuery ajax calls with Jasmine用 Jasmine 模拟 jQuery ajax 调用
【发布时间】:2017-01-25 12:28:55
【问题描述】:

我正在使用 Jasmine 2.5.2 为使用 jQuery 3.1.1 执行 Ajax 请求的代码编写单元测试。我想模拟 Ajax 调用,提供我自己的响应状态和文本。

我正在使用 Jasmine ajax 插件 (https://github.com/pivotal/jasmine-ajax)。

按照https://jasmine.github.io/2.0/ajax.html 上的示例,它使用了 XMLHttpRequest 对象,工作正常。

describe("mocking ajax", function() {
    describe("suite wide usage", function() {
        beforeEach(function() {
            jasmine.Ajax.install();
        });

        afterEach(function() {
            jasmine.Ajax.uninstall();
        });

        it("specifying response when you need it", function() {
            var doneFn = jasmine.createSpy("success");

            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(jasmine.Ajax.requests.mostRecent().url).toBe('/some/cool/url');
            expect(doneFn).not.toHaveBeenCalled();

            jasmine.Ajax.requests.mostRecent().respondWith({
                "status": 200,
                "contentType": 'text/plain',
                "responseText": 'awesome response'
            });
            expect(doneFn).toHaveBeenCalledWith('awesome response');                      
        });
    });
});

注意:这与记录的示例略有不同,必须将 jasmine.Ajax.requests.mostRecent().response() 更改为 jasmine.Ajax.requests.mostRecent().respondWith()

当我使用 jQuery Ajax 时,从不调用 doneFn。

describe("mocking ajax", function() {
    describe("suite wide usage", function() {
        beforeEach(function() {
            jasmine.Ajax.install();
        });

        afterEach(function() {
            jasmine.Ajax.uninstall();
        });

        it("specifying response when you need it", function() {
            var doneFn = jasmine.createSpy("success");

            $.ajax({
                method: "GET",            
                url: "/some/cool/url"})
            .done(function(result) {
                doneFn(result);
            });
            expect(doneFn).toHaveBeenCalledWith('awesome response');                      
        });
    });
});

茉莉花说

Jasmine-Ajax 在 XMLHttpRequest 对象,所以应该与其他兼容 执行 ajax 请求的库。

$.ajax 从 1.4.x 返回 jqXHR 而不是 XMLHttpRequest - 这会破坏 Jasmine Ajax 的支持吗?

【问题讨论】:

  • 在您的第二个示例中,您从未真正为请求定义响应...

标签: jquery ajax unit-testing jasmine jasmine-ajax


【解决方案1】:

这里有几种方法可以在你的 jasmine 测试中模拟 ajax

方法 A:

  • 使用模拟 ajax.js,您可以模拟全局 xhr 对象,如 doc 中所述
  • 但是,您需要在成功函数上指定一个 spy 并让它调用(如下面的代码所示)
  • 看到它在行动here

    describe('ajax test suite', function() {
    
      beforeEach(function() {
        jasmine.Ajax.install();
      });
    
      afterEach(function() {
        jasmine.Ajax.uninstall();
      });
    
      it('sample test', function() {
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function(args) {
          if (this.readyState == this.DONE) {
            testObj.successFunction(this.responseText);
          }
        };
        spyOn(testObj, 'successFunction').and.callThrough();
        xhr.open("GET", "https://jsonplaceholder.typicode.com/posts/1");
        xhr.send();
        expect(jasmine.Ajax.requests.mostRecent().url).toBe('https://jsonplaceholder.typicode.com/posts/1');
        expect(testObj.successFunction).not.toHaveBeenCalled();
        jasmine.Ajax.requests.mostRecent().respondWith({
          "status": 200,
          "contentType": 'text/plain',
          "responseText": 'awesome response'
        });
        expect(testObj.successFunction).toHaveBeenCalledWith('awesome response');
      });
    });
    

方法 B:

  • 直接模拟 $.ajax 对象。无论是 get/post/load 还是来自 jquery 的任何 ajax 风格,您都可以简单地模拟 $.ajax,如下所示。

    var testObj = {
      ajaxFunction : function(url){     
       $.ajax({url : url}).done(this.successFunction.bind(this));
      },
      successFunction : function(data){
       console.log(data);
     }
    }
    
    describe('ajax test suite', function(){
        it('sample test', function(){
         testObj.ajaxFunction('https://jsonplaceholder.typicode.com/posts/1');
         spyOn($, 'ajax').and.callFake(function(e) {
              return $.Deferred().resolve({'text':'this a a fake response'}).promise();
         });
         spyOn(testObj, 'successFunction').and.callThrough();
         testObj.ajaxFunction('https://jsonplaceholder.typicode.com/posts/1');
         expect(testObj.successFunction).toHaveBeenCalledWith({'text':'this a a fake response'});
      });
    });
    

【讨论】:

  • 您的第二种方法在这里对我有用。 fake 还可以返回$.Deferred().reject(obj) 来测试 Ajax 错误,其中 obj 具有带有错误文本的 responseText 属性。
  • @Lozzer 是正确的。使用 deferred,您可以根据您的要求解决/拒绝。
  • 如果我们想要或需要在我们的测试中调用其他 jQuery AJAX 调用怎么办?我开始觉得这是唯一可行的解​​决方案,我对此一点也不高兴。如果我只是要模拟整个 $.ajax 函数,那么首先使用 jasmine.Ajax 有什么意义?
【解决方案2】:

mock $.ajax对象时,传入的参数可以直接用来触发成功或失败,无需经过promise接口。

spyOn($, 'ajax').and.callFake(function (options) {
    var result = undefined, status, xhr;
    options.success(result, status, xhr);
});

spyOn($, 'ajax').and.callFake(function (options) {
    var xhr = undefined, status, error;
    options.error(xhr, status, error);
});

【讨论】:

    【解决方案3】:

    除了danronmoon答案(方法B):

    我在spyOn($, 'ajax') 中添加了options.success(testData) 以触发this.processResponseData

    Fn 测试:

     var objUnderTest = {};
     objUnderTest.makeRequest(requestUrl) {
        $.ajax(requestUrl)
          .then(response => {
            this.processResponseData(response.data[0]) // get data from first array item
          })
      }
    

    测试:

      describe('objUnderTest.makeRequest', () => {
        const testData = {data: [{'text': 'this a a fake response'}]};
    
        let processResponseData;
        beforeAll(() => {
          spyOn($, 'ajax').and.callFake(function (options) {
            options.success(testData)
            return $.Deferred().resolve(testData).promise();
          });
          processResponseData = spyOn(objUnderTest, 'processResponseData')
        })
    
        it('should make ajax call', () => {
          objUnderTest.makeRequest({success: stockTicker.processResponseData});
    
          expect(processResponseData).toHaveBeenCalled();
        });
      });
    

    【讨论】:

      猜你喜欢
      • 2014-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多