单元测试返回TypeError:undefined不是构造函数错误消息

时间:2016-09-24 18:08:49

标签: javascript angularjs unit-testing karma-jasmine

我正在尝试在Jasmine中编写一个简单的测试来测试SearchCtrl。但是我不确定如何构建单元测试,因为这是我第一次使用它。这是我到目前为止所做的:

services.js

中的

function CategoryService($http) {

    this.getCategoryList = function () {

        return $http({
            method: 'GET',
            url: server + '/api/category/',
        });

    }

}

示例输出:

[
    {
        "id": "1551742a-5963-4d40-9abb-af7a6fb4ec5d",
        "category": "Coach"
    }
]
controller.js

中的

function SearchCtrl($scope, CategoryService) {

    CategoryService.getCategoryList().then(function(dataResponse) {
        $scope.categories = dataResponse.data;
    });

}

search.controller.tests.js

describe('SearchCtrl', function() {

  var $controller, scope, categoryServiceMock;

  // TODO: Load the app module
  beforeEach(module('app.controllers'));
  beforeEach(module('app.services'));

  // TODO: Instantiate the controller and mocks
  beforeEach(inject(function($controller, categoryServiceMock) {
    scope = $rootScope.$new();

    // mock categoryService
    categoryServiceMock = {
      login: jasmine.createSpy('category spy')
    };

    // instantiate SearchCtrl
    controller = $controller('SearchCtrl', {
      $scope: scope,
      'CategoryService': categoryServiceMock
    })

  }));

  describe('#searchList', function() {

    // TODO: Call getCategoryList() on the controller
    it('should call getCategoryList on categoryService', function() {
      expect(categoryServiceMock.getCategoryList).toHaveBeenCalledWith('Coach');
    });

    describe('once the search is executed', function() {
      it('if successful, return a list of categories', function() {

        // TODO: Mock response from CategoryService
        expect(categoryServiceMock.getCategoryList).toHaveBeenCalledWith('Coach');
      });
    });
  });
});

目前返回错误:

Error: [$injector:unpr] Unknown provider: categoryServiceMockProvider <- categoryServiceMock

我该如何解决?此外,我是否以正确的方式构建了单元测试?

更新25/09

我将测试更改为以下内容:

describe('SearchCtrl', function() {

  var scope;
  var CategoryServiceMock;
  var SearchCtrl;

  // Load the app module
  beforeEach(module('dingocv.controllers', ['dingocv.services']));

  // Define CategoryServiceMock
  beforeEach(function() {
    CategoryServiceMock = {
      getCategoryList: function() {}
    };
  });

  // Inject required services and instantiate the controller
  beforeEach(inject(function($rootScope, $controller) {
    scope = $rootScope.$new();
    SearchCtrl = $controller('SearchCtrl', {
      $scope: scope,
      CategoryService: CategoryServiceMock
    });
  }));

  // Tests

  describe('#searchList', function() {
    it('should call registerBook Parse Service method', function () {
      spyOn(CategoryServiceMock, 'getCategoryList').andCallThrough();
      scope.getCategoryList();
    })
  });

});

我现在收到此错误:

PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 0 of 1 SUCC
PhantomJS 2.1.1 (Windows 8 0.0.0) SearchCtrl #searchList should call registerBook Parse Service method FAILED
        forEach@D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:13691:24
        loadModules@D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:17878:12
        createInjector@D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:17800:30
        workFn@D:/myapp-mobile/www/lib/angular-mocks/angular-mocks.js:3074:60
        loaded@http://localhost:9876/context.js:151:17
        D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:17918:53
        TypeError: undefined is not a constructor (evaluating 'spyOn(CategoryServiceMock, 'getCategoryList').andCallThrough()') in unit-tests/search.controller.tests.js (line 30)
        unit-tests/search.controller.tests.js:30:67
        loaded@http://localhost:9876/context.js:151:17
PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 1 of 1 (1 FPhantomJS 2.1.1 (Windows 8 0.0.0): Executed 1 of 1 (1 FAILED) ERROR (0.04 secs / 0.021 secs)
25 09 2016 09:48:38.940:INFO [watcher]: Changed file "D:/myapp-mobile/www/tests/unit-tests/search.controller.tests.js".
PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 0 of 1 SUCCPhantomJS 2.1.1 (Windows 8 0.0.0) SearchCtrl #searchList should call registerBook Parse Service method FAILED
        forEach@D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:13691:24
        loadModules@D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:17878:12
        createInjector@D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:17800:30
        workFn@D:/myapp-mobile/www/lib/angular-mocks/angular-mocks.js:3074:60
        loaded@http://localhost:9876/context.js:151:17
        D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:17918:53
        TypeError: undefined is not a constructor (evaluating 'spyOn(CategoryServiceMock, 'getCategoryList').andCallThrough()') in unit-tests/search.controller.tests.js (line 30)
        unit-tests/search.controller.tests.js:30:67
        loaded@http://localhost:9876/context.js:151:17
PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 1 of 1 (1 FPhantomJS 2.1.1 (Windows 8 0.0.0): Executed 1 of 1 (1 FAILED) ERROR (0.035 secs / 0.007 secs)
25 09 2016 09:48:42.307:INFO [watcher]: Changed file "D:/myapp-mobile/www/tests/unit-tests/search.controller.tests.js".
PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 0 of 1 SUCCPhantomJS 2.1.1 (Windows 8 0.0.0) SearchCtrl #searchList should call registerBook Parse Service method FAILED
        forEach@D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:13691:24
        loadModules@D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:17878:12
        createInjector@D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:17800:30
        workFn@D:/myapp-mobile/www/lib/angular-mocks/angular-mocks.js:3074:60
        loaded@http://localhost:9876/context.js:151:17
        D:/myapp-mobile/www/lib/ionic/js/ionic.bundle.js:17918:53
        TypeError: undefined is not a constructor (evaluating 'spyOn(CategoryServiceMock, 'getCategoryList').andCallThrough()') in unit-tests/search.controller.tests.js (line 30)
        unit-tests/search.controller.tests.js:30:67
        loaded@http://localhost:9876/context.js:151:17
PhantomJS 2.1.1 (Windows 8 0.0.0): Executed 1 of 1 (1 FPhantomJS 2.1.1 (Windows 8 0.0.0): Executed 1 of 1 (1 FAILED) ERROR (0.069 secs / 0.008 secs)

1 个答案:

答案 0 :(得分:2)

您只是简单地使用所有这些注射使测试变得复杂。这是一个简单的工作示例:

服务模块

angular.module('dingocv.services', [])
.service('CategoryService', ['$http', function CategoryService($http) {
    this.getCategoryList = function () {
        //Wasn't sure what server was here. So created a variable here. Feel free to change this.
        var server = 'https://www.example.com';
        return $http({
            method: 'GET',
            url: server + '/api/category/',
        });
    }
}]);

控制器模块

angular.module('dingocv.controllers', ['dingocv.services'])
.controller('SearchCtrl', ['$scope', 'CategoryService', function SearchCtrl($scope, CategoryService) {
    CategoryService.getCategoryList().then(function(dataResponse) {
        $scope.categories = dataResponse.data;
    });
}]);

<强>测试

describe('SearchCtrl', function() {
    var response;
    response = {
        status: 200,
        data: [{
            "id": "1551742a-5963-4d40-9abb-af7a6fb4ec5d",
            "category": "Coach"
        }]
    };

    //Injecting the modules that would then be used in the tests
    beforeEach(module('dingocv.services'));
    beforeEach(module('dingocv.controllers'));

    beforeEach(inject(function($controller, $rootScope, _CategoryService_) {
        $scope = $rootScope.$new();
        CategoryService = _CategoryService_;

        //Creating a spy for this method so that it doesn't call the original Service method.
        spyOn(CategoryService, 'getCategoryList').and.callFake(function(){
            return{
                then: function(successCallback){
                    successCallback(response);
                }
            }
        });

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

    describe('Initialization', function() {
        it('should initialize the controller\'s scope with categories', function(){
            expect(CategoryService.getCategoryList).toHaveBeenCalled();
            expect($scope.categories).toBeDefined();
            expect($scope.categories.length).toEqual(1);
            expect($scope.categories[0].id).toEqual(response.data[0].id);
            expect($scope.categories[0].category).toEqual(response.data[0].category);
        });
    });
});

希望这有帮助。