AngularJS 测试和 $http

AngularJS Testing and $http

所以我想弄清楚如何为我的 angular 控制器编写单元测试。我正在使用业力作为我的跑步者。我能够编写 1 个成功的测试,但每次我尝试编写另一个测试时,它都会对我大喊大叫,说是意外的电话等等。

这是我正在尝试测试的控制器。

(function (angular) {
'use strict';
var ngModule = angular.module('myApp.dashboardCtrl', []);
ngModule.controller('dashboardCtrl', function ($scope, $http) {

    //"Global Variables"
    var vm = this;
    vm.success = false;
    vm.repos = [];

    //"Global Functions"
    vm.addRepository = addRepository;
    vm.listRepos = listRepos;

    //Anything that needs to be instantiated on page load goes in the init
    function init() {
        listRepos();
    }
    init();

    // Add a repository
    function addRepository(repoUrl) {
        $http.post("/api/repo/" + encodeURIComponent(repoUrl)).then(function (){
            vm.success = true;
            vm.addedRepo = vm.repoUrl;
            vm.repoUrl = '';
            listRepos();
        });
    }
    //Lists all repos
    function listRepos() {
        $http.get('/api/repo').then( function (response){
            vm.repos = response.data;

        });
    }
});
}(window.angular));

所以我为 listRepos() 编写了一个测试。它是这样的

describe('dashboardCtrl', function() {
var scope, httpBackend, createController;
// Set up the module
beforeEach(module('myApp'));

beforeEach(inject(function($rootScope, $httpBackend, $controller) {
    httpBackend = $httpBackend;
    scope = $rootScope.$new();
    createController = function() {
        return $controller('dashboardCtrl', {
            '$scope': scope
        });
    };
}));

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

it('should call listRepos and return all repos from the database', function() {
    var controller = createController();
    var expectedResponse = [{id: 12345, url: "https://github.com/myuser/myrepo.git"}];

    httpBackend.expect('GET', '/api/repo')
        .respond(expectedResponse);
    httpBackend.flush();

    scope.$apply(function() {
        scope.listRepos;
    });

    expect(controller.repos).toEqual(expectedResponse);
});

这行得通并且测试通过了。现在我的问题是我想编写另一个测试来测试另一个调用新 api 端点的函数。

这是我试图为 addRepository 编写的测试。

 it('should addRepository to the database', function() {
        var controller = createController();
        var givenURL = "https://github.com/myuser/myURLtoMyRepo.git";

        httpBackend.expect('POST', '/api/repo/' + encodeURIComponent(givenURL)).respond('success');
        httpBackend.flush();

        scope.$apply(function() {
            scope.addRepository(givenURL);
        });

        expect(controller.success).toBe(true);
        expect(controller.listRepos).toHaveBeenCalled();
    });

将此测试添加到规范时出现的错误是:

Error: Unexpected request: GET /api/repo
Expected POST /api/repo/https%3A%2F%2Fgithub.com%2Fmyuser%2FmyURLtoMyRepo.git
    at $httpBackend 
Error: [$rootScope:inprog] $digest already in progress
http://errors.angularjs.org/1.4.8/$rootScope/inprog?p0=%24digest

我正在使用的示例是 this one here

非常感谢任何建议或提示!

更新:

因此将我的功能更改为 return 来自 $http.post、

的承诺

我重写了我的第二个测试,并将我的第一个测试包装在描述它试图测试的函数的描述块中。

具有以下内容:

describe('addRepository', function () {

    it('should addRepository to the database', function () {
        var controller = createController();
        var givenURL = "https://github.com/myuser/myURLtoMyRepo.git";

        httpBackend.expect('POST', '/api/repo/' + encodeURIComponent(givenURL)).respond('success');

        scope.$apply(function () {
            scope.addRepository(givenURL);
        });
        httpBackend.flush();

        expect(controller.success).toBe(true);
    });
    it('should call listRepos', function() {
        var controller = createController();
        httpBackend.expect('GET', '/api/repo').respond('success');

        controller.controller().then(function (result) {
            expect(controller.listRepos).toHaveBeenCalled();

        });
        httpBackend.flush();
    });
});

我仍然得到错误:

Error: Unexpected request: GET /api/repo
Expected POST /api/repo/https%3A%2F%2Fgithub.com%2Fmyuser%2FmyURLtoMyRepo.git
    at $httpBackend 
Error: [$rootScope:inprog] $digest already in progress

还有

TypeError: 'undefined' is not a function (evaluating 'controller.controller()') 
Error: Unflushed requests: 1

显示 2 个测试失败。

flush 应该在函数调用之后出现。我还将函数更改为 return 来自 $http.post:

的承诺
    // Add a repository
    function addRepository(repoUrl) {
        return $http.post("/api/repo/" + encodeURIComponent(repoUrl)).then(function (){
            vm.success = true;
            vm.addedRepo = vm.repoUrl;
            vm.repoUrl = '';
            listRepos();
        });
    }

然后在测试中可以调用它,测试成功部分:

编辑

我把 controller.controller() 改成了你的。

it('should call listRepos', function() {
    // Your setup
    ctrl.addRepository().then(function(result) {
        expect(ctrl.listRepos).toHaveBeenCalled();
    });
});

编辑 2

我尽我所能模拟了你的代码和我为代码编写的测试:

(function () {
  'use strict';

  angular
    .module('myApp')
    .controller('DashboardController',DashboardController);

    DashboardController.$inject = ['$http'];

    function DashboardController($http) {

      var vm = this;
      vm.success = false;
      vm.repos = [];

      vm.addRepository = addRepository;
      vm.listRepos = listRepos;

      init();

      // Anything that needs to be instantiated on page load goes in the init
      function init() {
        vm.listRepos();
      } 

      // Add a repository
      function addRepository(repoUrl) {
        return $http.post('http://jsonplaceholder.typicode.com/posts/1.json').then(function (){
          vm.success = true;
          vm.addedRepo = vm.repoUrl;
          vm.repoUrl = '';
          vm.listRepos();
        });
      }
      // Lists all repos
      function listRepos() {
        return $http.get('http://jsonplaceholder.typicode.com/posts/1').then( function (response){
          vm.repos = response.data;
        });
      }
    };
}());

我在这里使用在线 JSONPlaceholder API 来模拟 HTTP 调用,因为我显然无法找到您所指的内容。对于测试(全部通过):

(function() {
    'use strict';

    fdescribe('DashBoardController', function() {
        var $rootScope, scope, ctrl, $httpBackend;
        beforeEach(module('myApp'));

        beforeEach(inject(function(_$rootScope_, _$httpBackend_,$controller) {
            $rootScope      = _$rootScope_; 
            scope           = $rootScope.$new();
            $httpBackend    =_$httpBackend_;

            ctrl = $controller('DashBoardController',{
                $scope: scope
            });
        }));

        beforeEach(function() {
            // Setup spies
            spyOn(ctrl,'listRepos');
        });

        describe('controller', function() {
            it('should be defined', function() {
                expect(ctrl).toBeDefined();
            });
            it('should initialize variables', function() {
                expect(ctrl.success).toBe(false);
                expect(ctrl.repos.length).toBe(0);
            });
        });

        describe('init', function() {

            it('should call listRepos', function() {
                $httpBackend.expectGET('http://jsonplaceholder.typicode.com/posts/1')
                    .respond({success: '202'});

                $httpBackend.expectPOST('http://jsonplaceholder.typicode.com/posts/1.json')
                    .respond({success: '202'});

                ctrl.addRepository().then(function(result) {
                    expect(ctrl.success).toBe(true);
                    expect(ctrl.repoUrl).toBe('');
                    expect(ctrl.listRepos).toHaveBeenCalled();
                });

                $httpBackend.flush();
            });
        });
    });

}());