【问题标题】:httpBackend Mock AJAX ES6 Promise in $q.when$q.when 中的 httpBackend 模拟 AJAX ES6 Promise
【发布时间】:2016-12-03 15:05:57
【问题描述】:

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

我一直在四处寻找,试图弄清楚这一点,但仍然不明白是什么导致了通话通过。我觉得好像 ES6 承诺中的 HTTP 请求是在“Angular 之外”发出的,所以 $httpBackend 不知道它/无法捕获它,尽管如果调用可能不是这样从一开始就在 $q 承诺中。谁能告诉我为什么这个电话会通过以及为什么一个简单的超时就可以正常工作?我在这里尝试了$scope.$apply$scope.$digest$httpBackend.flush() 的所有组合,但无济于事。

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

控制器

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");
        });
    });

【问题讨论】:

  • httpBackend 是一个模拟实现,它通过角度注入到 $http 服务中进行单元测试,如果您不使用 $http 服务发出请求,您将无法使用 httpBackend 捕捉它

标签: javascript angularjs ajax unit-testing jasmine


【解决方案1】:

感谢格雷厄姆的评论,由于我对几件事缺乏了解,我被带入了一个不同的兔子洞,我将在这里总结一下,以防有人最终陷入同样的​​境地......

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

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

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

    问题(在控制器的来源中):

     // 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!
     });
    

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-09-15
    • 2023-03-14
    • 2017-04-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多