【问题标题】:How to fake jquery.ajax() response?如何伪造 jquery.ajax() 响应?
【发布时间】:2011-07-13 11:06:08
【问题描述】:

我正在为进行 AJAX 调用的 JavaScript 编写一些 QUnit 测试。

为了隔离,我覆盖$.ajax 以将 AJAX 调用的参数数组写入变量。这可以测试方法如何使用 AJAX 函数,但我很难测试 $.load() 的成功处理程序

来自http://api.jquery.com/load/的文档:

当检测到成功响应时(即当 textStatus 为“success”或“notmodified”时),.load() 将匹配元素的 HTML 内容设置为返回的数据。

所以我试图返回一个对象,该对象包含与成功处理程序的变量同名的对象:

    //Mock ajax function
    $.ajax = function (param) {
        _mockAjaxOptions = param;
        var fakeAjaxSuccess = { responseText: "success", textStatus: "success", XMLHttpRequest: "success" };
        return fakeAjaxSuccess;
    };

但是这种方法没有奏效。

如何复制成功的 AJAX 调用的行为?

【问题讨论】:

    标签: javascript jquery ajax unit-testing testing


    【解决方案1】:

    查看 jQuery 文档:您会看到 Ajax 设置提供了许多其他经过测试的条件。如果你让它们都指向你的 fakeAjaxSuccess,你可能会实现你的目标。

    或者,将您的 $.ajax 调用包装到它自己的函数中,让任何调用都简单地使用 fakeAjaxSuccess 对象调用您的事件处理程序。

    【讨论】:

      【解决方案2】:

      我认为下面的链接应该会有所帮助。至于参数我不太确定,但可能是。

      $.fn.ajax.success =  function (){
        ///the rest goest here
      }
      

      Override jQuery .val() function?

      【讨论】:

      • 没有任何意义......浏览器仍然会发出请求。这只会覆盖它看起来的成功函数。
      【解决方案3】:

      在阅读了@Robusto 和@Val 的启发后,我发现了一种有效的方法:

      //Mock ajax function
      $.ajax = function (param) {
          _mockAjaxOptions = param;
          //call success handler
          param.complete("data", "textStatus", "jqXHR");
      };
      

      我没有从任何真正的 $.ajax 代码引发事件或通过触发任何事件,而是让我的假 ajax 对象调用该函数(作为参数传入$.ajax())作为我的假函数的一部分。

      【讨论】:

      • 我会做同样的事情,尽管我将 $.ajax 调用隐藏在另一个方法后面以便在我的测试中更容易换出。
      • 虽然可能晚了,但你可能一直在寻找github.com/appendto/jquery-mockjax
      • @xhh 这是一个非常好的设置返回值的框架。
      • @PeterSmith 你为什么要添加另一个函数?由于 JS 是动态类型的,因此您可以就地覆盖它。除了封装之外,它给你带来了什么好处吗?
      • 那么你究竟如何处理这个似乎悬在空中的_mockAjaxOptions 未声明变量?
      【解决方案4】:

      这个问题已经有几年了,对于新版本的jQuery已经改变了一点。

      要使用 Jasmin 执行此操作,您可以尝试 Michael Falaga's approach

      解决方案

        function ajax_response(response) {
          var deferred = $.Deferred().resolve(response);
          return deferred.promise;
        }
      

      茉莉花

        describe("Test test", function() {
          beforeEach(function() {
            spyOn($, 'ajax').and.returnValue(
              ajax_response([1, 2, 3])
            );
          });
          it("is it [1, 2, 3]", function() {
            var response;
            $.ajax('GET', 'some/url/i/fancy').done(function(data) {
              response = data;
            });
            expect(response).toEqual([1, 2, 3]);
          });
        });
      

      没有茉莉花

        $.ajax = ajax_response([1, 2, 3]);
        $.ajax('GET', 'some/url/i/fancy').done(function(data) {
           console.log(data); // [1, 2, 3]
        });
       
      

      【讨论】:

      • +one 非常感谢。
      • 我得到ajax is not a function 是:{state: ƒ, always: ƒ, catch: ƒ, pipe: ƒ...
      • @Toskan 我遇到了同样的问题。我认为这个答案在 2015 年是正确的,但在我今天写这篇文章时,“return deferred.promise();”行返回一个对象。你想把它改成“return deferred.promise;”,所以它返回promise FUNCTION。
      【解决方案5】:

      使用闭包以虚拟响应覆盖 $.ajax

      在尝试了the accepted answerthe answer posted by user1634074 之后,我设计了这两者的简单而灵活的组合。

      最基本的形式……

      function ajax_response(response) {
        return function (params) {
          params.success(response);
        };
      }
      $.ajax = ajax_response('{ "title": "My dummy JSON" }');
      

      在上面的示例中,定义一个函数 ajax_response(),它接受一些 JSON 字符串作为参数(或任何数量的用于模拟响应的自定义参数)并返回一个匿名闭包函数,该函数将分配给 $.ajax单元测试的覆盖。

      匿名函数接受params 参数,该参数将包含传递给$.ajax 函数的设置对象。它使用传递给外部函数的参数来模拟来自服务器的响应。在此示例中,它总是通过简单地调用 success 回调并向其提供虚拟 JSON 来模拟来自服务器的成功响应。

      很容易重新配置……

      function ajax_response(response, success) {
        return function (params) {
          if (success) {
            params.success(response);
          } else {
            params.error(response);
          }
        };
      }
      
      // Simulate success
      $.ajax = ajax_response('{ "title": "My dummy JSON." }', true); 
      doAsyncThing(); // Function that calls $.ajax
      
      // Simulate error
      $.ajax = ajax_response('{ "error": "Who is the dummy now?" }', false); 
      doAsyncThing(); // Function that calls $.ajax
      

      我们可以在下面看到它的实际效果……

      /* FUNCTION THAT MAKES AJAX REQUEST */
      function doAsyncThing() {
        $.ajax({
          type: "POST",
          url: "somefile.php",
          // data: {…},
          success: function (results) {
            var json = $.parseJSON(results),
                html = $('#ids').html();
            $('#ids').html(html + '<br />' + json.id);
          }
        });
      }
      
      /* BEGIN MOCK TEST */
      // CREATE CLOSURE TO RETURN DUMMY FUNCTION AND FAKE RESPONSE
      function ajax_response(response) {
        return function (params) {
          params.success(response);
        };
      }
      
      var n = prompt("Number of AJAX calls to make", 10);
      
      for (var i = 1; i <= n; ++i) {
        
        // OVERRIDE $.ajax WITH DUMMY FUNCTION AND FAKE RESPONSE
        $.ajax = ajax_response('{ "id": ' + i + ' }');
        doAsyncThing();
      }
      /* END MOCK TEST */
      <script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
      <p id="ids">IDs:</p>

      【讨论】:

        【解决方案6】:

        在不影响 jQuery 的情况下根据需要模拟 $.ajax

        这里的答案很好,但特别需要构建对单个 API 调用的虚假响应,同时让所有其他 API 调用保持不变,直到构建后端服务,以便我可以继续在 UI 上构建内容。

        API 对象在底层使用 $.ajax,因此您可以像这样调用 API 方法:

        api.products({ price: { $lt: 150, tags: ['nike', 'shoes'] } })
        .done(function(json) {
          // do something with the data
        })
        .error(function(err) {
          // handle error
        });
        

        这个方法可以解决问题:

        function mockAjax(options) {
          var that = {
            done: function done(callback) {
              if (options.success)
                setTimeout(callback, options.timeout, options.response);
              return that;
            },
            error: function error(callback) {
              if (!options.success)
                setTimeout(callback, options.timeout, options.response);
              return that;
            }
          };
          return that;
        }
        

        然后在不触及 $.ajax 的情况下覆盖单个 api 调用:

        api.products = function() {
          return mockAjax({
            success: true,
            timeout: 500,
            response: {
              results: [
                { upc: '123123', name: 'Jordans' },
                { upc: '4345345', name: 'Wind Walkers' }
              ]
            }
          });
        };
        

        https://jsfiddle.net/Lsf3ezaz/2/

        【讨论】:

          【解决方案7】:

          这是一个简单的工作解决方案

          var set_ajax_response = function(data){
            $.ajax = $.Deferred().resolve(data).promise;
          }
          
          var data = [1,2,3];
          
          set_ajax_response(data);
          

          【讨论】:

          猜你喜欢
          • 2015-07-13
          • 1970-01-01
          • 2022-07-05
          • 1970-01-01
          • 2013-06-22
          • 1970-01-01
          • 2011-08-17
          • 2011-10-17
          • 1970-01-01
          相关资源
          最近更新 更多