使$ httpBackend忽略对服务器发出的任何请求

时间:2014-08-27 11:27:37

标签: javascript angularjs unit-testing jasmine httpbackend

我有以下控制器(请注意,在实例化时我明确调用$scope.getNotifications()):

bla.controller("myctrl", [
    "$scope", "$http", "configs", function ($scope, $http, configs) {

        $scope.getNotifications = function () {
            $http.get("bla/blabla").success(function (data) {

            });
        };

        $scope.removeNotification = function (notification) {
            var index = $scope.allNotifications.indexOf(notification);
            $scope.allNotifications.splice(index, 1);
        };

        $scope.getNotifications();
    }
]);

然后我做了一些单元测试(注意控制器在每个之前实例化):

describe("blaController", function () {

    var scope, $httpBackend;

    beforeEach(module('bla'));

    beforeEach(inject(function ($controller, $rootScope, _$httpBackend_) {
        scope = $rootScope.$new();
        $httpBackend = _$httpBackend_;
        $controller('blaCtrl', { $scope: scope });
    }));

 afterEach(function(){
    //assert
    $httpBackend.verifyNoOutstandingExpectation();
    $httpBackend.verifyNoOutstandingRequest();
 });

 it("should get all notifications from server when instantiated", function () {
    //arrange
    $httpBackend.expectGET("api/v1/notifications").respond(200, {});
    $httpBackend.flush();

    //act - done implicitly when controller is instantiated  
});

it("should store all notifications from server on the client when success call to server", function () {
    //arrange
    $httpBackend.whenGET("api/v1/notifications").respond(200, [{ a: 1, b: 2 }, { c: 3, d: 4 }]);
    $httpBackend.flush();

    //act - done implicitly when controller is instantiated

    //assert
    expect(scope.allNotifications).toEqual([{ a: 1, b: 2 }, { c: 3, d: 4 }]);
});

到目前为止,一切都很好。所有测试都通过。但是当我添加一个不需要任何HTTP调用的新测试(见下文)时,它会失败,因为在afterEach()中它会验证expecations,但removeNotification()中没有设置期望值。 这是来自业力的错误消息:   PhantomJS 1.9.7(Windows 8)notificationCenterController removeNotification应该从中删除给定的通知 e list FAILED         错误:意外请求:GET api / v1 / notifications         不再需要预期

it("should remove the given notification from the list", function () {
            //arrange
            var targetObj = { a: 2 };
            scope.allNotifications = [{ a: 1 }, targetObj, { a: 3 }];

            //act
            scope.removeNotification(targetObj);

            //assert
            expect(scope.allNotifications).toEqual([{ a: 1 }, { a: 3 }]);
        });

我的大多数测试都有http调用,因此将验证放在afterEach中是有道理的。我想知道我还有什么其他选择可以避免在N-1测试中复制粘贴afterEach主体。 有没有办法告诉$httpBackend忽略任何来电?

3 个答案:

答案 0 :(得分:3)

您可以将测试包装在下面的描述块中。

describe("blaController", function () {

    var scope, $httpBackend;

    beforeEach(module('bla'));

    beforeEach(inject(function ($controller, $rootScope, _$httpBackend_) {
        scope = $rootScope.$new();
        $httpBackend = _$httpBackend_;
        $controller('blaCtrl', { $scope: scope });
    }));

    describe('test http calls', function() {

        afterEach(function(){
            //assert
            $httpBackend.verifyNoOutstandingExpectation();
            $httpBackend.verifyNoOutstandingRequest();
        });

        it("should get all notifications from server when instantiated", function () {
            //arrange
            $httpBackend.expectGET("api/v1/notifications").respond(200, {});
            $httpBackend.flush();

            //act - done implicitly when controller is instantiated  
        });

        it("should store all notifications from server on the client when success call to server", function () {
        //arrange
            $httpBackend.whenGET("api/v1/notifications").respond(200, [{ a: 1, b: 2 }, { c: 3, d: 4 }]);
            $httpBackend.flush();

            //act - done implicitly when controller is instantiated

            //assert
            expect(scope.allNotifications).toEqual([{ a: 1, b: 2 }, { c: 3, d: 4 }]);
        });

    }); 

    describe('other tests', function(){
        it("should remove the given notification from the list", function () {
            //arrange
            var targetObj = { a: 2 };
            scope.allNotifications = [{ a: 1 }, targetObj, { a: 3 }];

            //act
            scope.removeNotification(targetObj);

            //assert
            expect(scope.allNotifications).toEqual([{ a: 1 }, { a: 3 }]);
        });
    });
});

答案 1 :(得分:1)

你可以在一个单独的套件中窥探$ http.get,这应该可行(下面的伪代码)。

 describe("backend", function() { // your code from before });

 describe("non-backend", function () {

    var scope, $http;

    beforeEach(module('bla'));

    beforeEach(inject(function ($controller, $rootScope, _$http_) {
        scope = $rootScope.$new();
        $http = _$http_;
        spyOn($http, 'get').and.callFake(function() {
            return { some: 'data' };
        });
        $controller('blaCtrl', { $scope: scope, $http: $http });
    }));

    it("should remove the given notification from the list", function () {
        //arrange
        var targetObj = { a: 2 };
        scope.allNotifications = [{ a: 1 }, targetObj, { a: 3 }];

        //act
        scope.removeNotification(targetObj);

        //assert
        expect(scope.allNotifications).toEqual([{ a: 1 }, { a: 3 }]);
    });
});

答案 2 :(得分:1)

由于您在构造函数中创建了http请求,并且每次运行测试时都在运行构造函数,因此httpBackend应该准备好每次都响应此请求。

所以在你的beforeEach区块中,你应该设置:

notificationsRequest = $httpBackend.expectGET("api/v1/notifications").respond(200, {});

在您的测试用例中,您可以通过再次调用响应方法来更改此响应,这会覆盖响应集,如下所示:

it("should store all notifications from server on the client when success call to server", function () {
    //arrange
    notificationsRequest.respond(200, [{ a: 1, b: 2 }, { c: 3, d: 4 }]);
    $httpBackend.flush();

    //act - done implicitly when controller is instantiated

    //assert
    expect(scope.allNotifications).toEqual([{ a: 1, b: 2 }, { c: 3, d: 4 }]);
});

现在,您需要做的就是确保在每次测试后刷新响应。鉴于,这可能不方便,但如果你想要verifyNoOutstandingRequest,必须这样做,因为根据你的构造函数的定义,会有一个未完成的请求。我建议你通过将测试结构化为嵌套套件,以及那些没有测试通知检索的测试,在他们自己的beforeEach块中刷新请求。