javascript - $q.when 中的 httpBackend Mock AJAX ES6 Promise

标签 javascript angularjs ajax unit-testing jasmine

我正在尝试模拟对 JSONP GET 请求的响应,该请求是使用一个返回 ES6 promise 的函数发出的,我已将其包装在 $q.when() 中。代码本身工作得很好,但是,在单元测试中,请求没有被 $httpBackend 捕获,而是直接到达实际的 URL。因此,当调用 flush() 时,我收到一条错误消息,指出 Error: No pending request to flush !。 JSONP 请求是通过 ES6 promise 中的 jQuery 的 $.getJSON() 发出的,因此我选择通过提供正则表达式而不是硬编码 URL 来 try catch 所有传出请求。

一段时间以来,我一直在四处搜索,试图弄清楚这个问题,但仍然不明白是什么原因导致通话通过。我觉得好像 ES6 promise 中的 HTTP 请求是在“Angular 之外”发出的,所以 $httpBackend 不知道它/无法捕获它,尽管如果正在调用,情况可能并非如此从一开始就在 $q promise 中。任何人都可以告诉我为什么要进行此调用以及为什么简单的超时就可以正常工作吗?我在这里尝试了 $scope.$apply$scope.$digest$httpBackend.flush() 的所有组合,但是无济于事。

也许一些代码会更好地解释它......

Controller

function homeController() {
    ...
    var self = this;
    self.getData = function getData() {
        $q.when(user.getUserInformation()).then(function() {
            self.username = user.username;
        });
    };
}

单元测试

...

beforeEach(module('home'));

describe('Controller', function() {
    var $httpBackend, scope, ctrl;

    beforeEach(inject(function(_$httpBackend_, $rootScope, $componentController) {
        $httpBackend = _$httpBackend_;
        scope = $rootScope.$new(); // used to try and call $digest or $apply
        // have also tried whenGET, when('GET', ..), etc...
        $httpBackend.whenJSONP(/.*/)
                    .respond([
                        {
                            "user_information": {
                                "username": "TestUser",
                            }
                        }
                    ]);
        ctrl = $componentController("home");
    }));

    it("should add the username to the controller", function() {
        ctrl.getData(); // make HTTP request
        $httpBackend.flush(); // Error: No pending request to flush !
        expect(ctrl.username).toBe("TestUser");
    });
});

...

但是出于某种原因,这是可行的:

    it("should add the username to the controller", function() {
        ctrl.getData(); // make HTTP request
        setTimeout(() => {
            // don't even need to call flush, $digest, or $apply...?
            expect(ctrl.username).toBe("TestUser");
        });
    });

最佳答案

感谢 Graham 的评论,由于我不了解几件事,我被带到了一个不同的兔子洞里,我将在这里总结一下,以防有人遇到同样的情况......

  1. 我没有完全理解 JSONP 的工作原理。它根本不依赖于 XmlHttpRequest(参见 here )。我没有试图通过 JSONP 来摆弄对这些请求的模拟响应,而是简单地切换了我正在使用的禁用 JSONP 的代码上的“调试”标志,因此调用是通过 XHR 对象进行的(如果真实的话,这将使 same origin policy 失败需要来自此外部 API 的响应)。
  2. 我没有尝试使用 jasmine-ajax,而是简单地在 jQuery 的 getJSON 上设置了一个 spy 并返回了一个模拟响应。这最终发送了对 ES6 promise 的模拟响应,但是由于某种原因,没有调用由包装 ES6 promise 产生的 $q promise 对象的 then 函数(也没有调用任何其他错误处理功能,甚至 finally)。我也试过在几乎任何地方调用 $scope.$apply() 可能会有帮助,但无济于事。

    基本实现(在单元测试中):

     ...
     spyOn($, 'getJSON').and.callFake(function (url, success) {
         success({"username": "TestUser"}); // send mock data
     });
     ctrl.getData(); // make GET request
     ...
    

    问题(在 Controller 的源代码中):

     // user.getUserInformation() returns an ES6 promise
     $q.when(user.getUserInformation()).then(function() {
         // this was never being called / reached! (in the unit tests)
     });
    
  3. 最终,我使用 #2 的实现来发送数据,并且只是将单元测试中的断言包装在超时内,没有指定持续时间。我意识到这不是最优的,希望不是应该如何完成,但是在尝试了很多小时之后我已经达到了我的极限并放弃了。如果有人知道如何对此进行改进,或者为什么没有调用 then,我很想听听。

    单元测试:

     ...
     ctrl.getData(); // make GET request
     setTimeout(() => {
         expect(ctrl.username).toBe("TestUser"); // works!
     });
    

关于javascript - $q.when 中的 httpBackend Mock AJAX ES6 Promise,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38649637/

相关文章:

javascript - 在 ajax 完成时更新 jQuery 变量

javascript - Angular .js : $sanitize fails on $injector:modulerr using exact code example from angular site

javascript - 如何在 Angular.js 中获取我自己定义的服务的拦截器的响应?

javascript - 从缓存中预加载脚本

javascript - 将所有选中复选框的名称属性放入数组 - JavaScript

php - 在网页上实时显示通知和消息的最佳方式?

javascript - 通过 AJAX 加载 SPA 网页

java - 属性更改时 JSF ajax 重新渲染组件

javascript - Mobile Firefox 上的 Angular 4 应用程序呈现问题

css - 颜色代码未在同一行对齐