【问题标题】:testing angularjs $httpbackend and $resource测试 angularjs $httpbackend 和 $resource
【发布时间】:2014-09-08 20:25:13
【问题描述】:

*更新 - 见帖子结尾*

我有一个依赖于服务的控制器。该服务封装了$resource。这是代码

app.controller('charactersController', function ($scope, $routeParams, $location, marvelRepository) {
    var page = parseInt($routeParams.pageNumber);

    marvelRepository.fetch(page - 1).then(function (data) {
        $scope.characters = data.results;
        $scope.paging = {
            page: page,
            total: data.total,
            pageSize: data.limit
        };
    });

    $scope.goto = function (pageNumber) {
        $location.path('/characters/' + pageNumber);
    };
});

marvel.service('marvelRepository', function (hash, $resource) {
    var extended = ng.extend(hash.authenticate(), { id: '@id' });
    var characters = $resource('http://gateway.marvel.com/v1/public/characters/:id', extended);

    return {
        get: function (characterId) {
            return characters
                .get({ id: characterId })
                .$promise
                .then(function (response) {
                    if (response.code !== 200) {
                        throw response;
                    }

                    return response.data.results[0];
                });
        },
        fetch: function (pageIndex) {
            return characters
                .get({ limit: 20, offset: pageIndex * 20 })
                .$promise
                .then(function (response) {
                    if (response.code !== 200) {
                        throw response;
                    }

                    return response.data;
                });
        }
    };
});

哈希依赖为 marvel api 创建身份验证参数:公钥、时间戳和哈希值。

网站正常工作,我可以显示字符列表。现在我正在尝试编写测试(因为 Angular 促进了简单的可测试性......)

这是我的测试

describe("Angular Demo - Test", function () {
    beforeEach(angular.mock.module('demo'));

    describe('requiring Marvel respository', function() {
        var url = 'http://gateway.marvel.com/v1/public/characters?apikey=123&hash=abc123&limit=20&offset=0&ts=20140601073322';
        var httpBackend, location, scope, controller;

        beforeEach(module(function (hashProvider) {
            hashProvider.override({
                apikey: '123',
                ts: '20140601073322',
                hash: 'abc123'
            });
        }));

        beforeEach(inject(function ($httpBackend, $location, $rootScope, $controller) {
            controller = $controller;
            location = $location;
            scope = $rootScope.$new();
            httpBackend = $httpBackend;
        }));

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

        describe('charactersController', function () {
            var characters;

            beforeEach(function() {
                characters = new Array(20);

                httpBackend.when('get', url).respond({ data: { results: characters, offset: 2, limit: 20, total: 100 } });

            });

            it('should be able to get a page of characters', function () {
                httpBackend.expect('get', url);

                controller('charactersController', { $scope: scope, $routeParams: { pageNumber: 1 } });

                httpBackend.flush();

                expect(scope.characters).toBe(characters);
            });

            it('should be able to configure paging', function () {
                 httpBackend.expect('get', url);

                controller('charactersController', { $scope: scope, $routeParams: { pageNumber: 1 } });

                httpBackend.flush();

                expect(scope.paging.total).toBe(100);
                expect(scope.paging.page).toBe(1);
                expect(scope.paging.pageSize).toBe(20);
            });

            it('should be able to navigate to another page of characters', function () {
                controller('charactersController', { $scope: scope });

                scope.goto(5);

                expect(location.path()).toBe('/characters/5');
            });
        });
    });
});

当我执行测试时,我得到以下结果

错误:意外请求:GET http://gateway.marvel.com/v1/public/characters/1?apikey=123&hash=abc123&ts=20140601073322

预期得到 http://gateway.marvel.com/v1/public/characters/1?apikey=123&hash=abc123&ts=20140601073322

错误:意外请求:GET http://gateway.marvel.com/v1/public/characters/1?apikey=123&hash=abc123&ts=20140601073322

预期得到 http://gateway.marvel.com/v1/public/characters/1?apikey=123&hash=abc123&ts=20140601073322 在 $httpBackend (/tests/angular-mock.js:1172:13) 在 sendReq (/js/angular/angular.js:8286:9) 在 $http.serverRequest (/js/angular/angular.js:8027:16) 在 WrappedCallback (/js/angular/angular.js:11574:81) 在 WrappedCallback (/js/angular/angular.js:11574:81) 在 /js/angular/angular.js:11660:26 在 Scope.$eval (/js/angular/angular.js:12724:28) 在 Scope.$digest (/js/angular/angular.js:12536:31) 在 Function.$httpBackend.flush (/tests/angular-mock.js:1447:20) 在对象。 (/tests/tests.js:113:29)

如果我在整个代码中添加控制台语句。我可以看到 marvelRepository.fetch(1) 正在被调用。但是.$promise.then() 似乎没有执行。

对我来说没有意义的是错误的第一两行。首先它说明发出了一个意外的请求,然后它表示对 url 进行了预期的调用。而且是同一个网址。

对我忽略的内容有什么想法吗?

*更新* 我更新了测试的结构。设置whenexpect 调用。

我还发现,如果我将后端调用从 when('get', ... 更改为 when('GET', ...,我会收到以下测试错误

undefined: undefined

没有堆栈跟踪或行号,但未定义。查看它指出的原始错误

Error: Unexpected request: GET url....
Expected get url...

所以我想知道它是否因为比较请求方法的大小写敏感而失败。但是,我认为这是一个时间问题。注入服务并调用方法,但是 promise.then(cb) 不执行。但也许它没有触发是因为请求方法不匹配所以 $httpbackend 没有返回?

【问题讨论】:

    标签: angularjs unit-testing


    【解决方案1】:

    在 BeforeEach 中,您需要使用 .when() 而不是 .expect()

    httpBackend
    .when('GET', 'http://gateway.marvel.com/v1/public/characters?apikey=123&hash=abc123&limit=20&offset=0&ts=20140601073322')
    .respond({ data: { results: characters, offset: 2, limit: 20, total: 100 } });
    

    使用.expect() 通常在断言中完成,而不是设置。

    【讨论】:

    • when 和 expect 之间我能看到的唯一区别是错误的第二行。如果我使用when,它会说“不再有请求”。如果我使用expect,它会说“预期的 gateway.marvel....”
    • 第一行错误状态“意外的请求”我是否过早地设置了后端?
    • 除非您必须覆盖 URL,否则不需要您的内部 expect()。 HTTP 动词的大小写很重要,因此请始终使用大写字母。为了帮助隔离哪个测试失败,您可以 xit() 除了一个(如果您使用 Jasmine 1.x)之外的所有测试或使用 iit() 只运行一个测试(如果您使用 Jasmine 2.x) .一次只专注于一项测试,它应该有助于解决问题。
    • 感谢@MBielksi!我能够根据这些反馈进一步简化测试。
    【解决方案2】:

    终于找到了解决办法。问题是使用承诺和延迟执行。我还需要打电话给$scope.apply() 和'$httpBackend.flush()'

    这是一个合格的测试

    describe('characterController', function () {
        var characterUrl = 'http://gateway.marvel.com/v1/public/characters/1';
        var character;
    
        beforeEach(function () {
            character = { name: 'Wolverine'};
    
            httpBackend.whenGET(characterUrl).respond({ data: { results: [character] } });
        });
    
        it('should be able to get character with id of 1', function () {
            controller('characterController', { $scope: scope, $routeParams: { id: 1 } });
    
            scope.$apply();//execute promises
            httpBackend.flush(); //process http requests
    
            expect(scope.character).toEqual(character);
        });
    });
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-10-17
      • 1970-01-01
      • 2015-10-22
      • 1970-01-01
      • 2013-05-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多