$ q.when中的httpBackend Mock AJAX ES6承诺

时间:2016-07-29 02:29:46

标签: javascript angularjs ajax unit-testing jasmine

我正在尝试模拟对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");
        });
    });

1 个答案:

答案 0 :(得分:0)

感谢格雷厄姆的评论,由于我对一些事情缺乏了解,我被带到了另一个不同的兔子洞,以防万一有人在同样的情况下结束...

  1. 我没有完全理解JSONP是如何工作的。它根本不依赖于XmlHttpRequest(参见here)。而不是试图通过JSONP嘲笑对这些请求的模拟响应,我只是简单地切换了" debug"我正在使用哪些禁用JSONP的代码上的标志,因此调用是通过XHR对象进行的(如果需要来自此外部API的真实响应,这将使same origin policy失败。)
  2. 我没有尝试使用jasmine-ajax,而只是在jQuery的getJSON上设置间谍并返回模拟响应。这最终发送了对ES6承诺的模拟响应,但由于某种原因,包装ES6承诺导致的$ q promise对象的then函数没有被调用(也没有任何其他错误处理函数) ,甚至finally)。我也尝试在$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)
     });
    

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

    then
  3. 最终,我使用了#2的实现来发送数据,并在超时内将断言包装在单元测试中,没有指定持续时间。我意识到这不是最佳的,并且希望它不应该如何完成,但经过几个小时的努力,我已达到极限并放弃了。如果有人知道如何改进这一点,或者为什么 ... ctrl.getData(); // make GET request setTimeout(() => { expect(ctrl.username).toBe("TestUser"); // works! }); 没有被召唤,我真诚地希望听到它。

    单元测试:

    export default Test;
相关问题