在AngularJS中测试Controller Service调用

时间:2015-03-06 18:46:08

标签: angularjs unit-testing service controller karma-runner

我遇到以下情况的问题:

我有一个Controller进行服务调用,以获取不同的可用语言和问候语。 我想测试这个控制器,我已根据以下网站和文章编写的测试:

http://www.yearofmoo.com/2013/01/full-spectrum-testing-with-angularjs-and-karma.html#testing-controllers

Angular unit-test controllers - mocking service inside controller

http://jasmine.github.io/2.2/introduction.html

和当然AngularJs文件

但我觉得我做错了一些事情或者过度测试。

在我写的那些中,前3个传球,但第4个传球(在我眼中是最重要的传球)失败。

有人可以如此友好地帮助我或指出我正确的方向。 似乎我读过的每篇文章都说明了测试的内容和方法。

控制器

angular.module('app')
.controller('MainCtrl', function ($scope, LanguagesService) {
    $scope.languages = LanguagesService.getAll();
});

服务

angular.module('app')
  .factory('LanguagesService', function () {
   var lang = {};

   lang.greetings = [
      'Welkom bij,',
      'Bienvenu chez'
   ];

   lang.languages = [
     {
       name: 'Nederlands',
       code: 'nl'
     },
     {
       name: 'Français',
       code: 'fr'
     }
   ];

   return {
     getAll: function () {
        return lang;
     }
   };

});

我对控制器的单元测试

describe('Controller: MainCtrl', function () {

// load the controller's module
beforeEach(module('app'));

var MainCtrl,
      scope,
      LanguagesService;

// Initialize the controller and a mock scope
beforeEach(inject(function ($controller, $rootScope, _LanguagesService_) {
  scope = $rootScope.$new();
  LanguagesService = _LanguagesService_;
  MainCtrl = $controller('MainCtrl', {
    $scope: scope,
    'LanguagesService': LanguagesService
  });

  /*
  * Spy on service
  */
  spyOn(LanguagesService, 'getAll');
}));

/*
* Test 1: Is this test overkill ? As the tests wont run if the service is not injected
*/
it('should get an instance of LanguagesService', function() {
  expect(LanguagesService).toBeDefined();
});

it('should attach languages to the scope', function() {
  expect(scope.languages).not.toBe(null);
});

it('should have the same amount of languages as greetings', function() {
  expect(scope.languages.languages.length).toBe(scope.languages.greetings.length);
});

/*
* Test 4: This test fails
*/
it('should have called LanguagesService method getAll', function() {
  expect(LanguagesService.getAll).toHaveBeenCalled();
});

});

2 个答案:

答案 0 :(得分:3)

describe('Controller: MainCtrl', function () {

    // load the controller's module
    beforeEach(module('app'));

    var MainCtrl,
      scope,
      LanguagesService;

    var createController;
    var spy;

    // Initialize the controller and a mock scope
    beforeEach(inject(function ($controller, $rootScope, _LanguagesService_) {
        scope = $rootScope.$new();
        LanguagesService = _LanguagesService_;
        MainCtrl = $controller('MainCtrl', {
            $scope: scope,
            'LanguagesService': LanguagesService
        });

        createController = function () {
            return $controller('MainCtrl', {
                '$scope': scope,
                'LanguagesService': LanguagesService
            });
        };

        /*
        * Spy on service
        */
        spy = spyOn(LanguagesService, 'getAll');
    }));

    /*
    * Test 1: Is this test overkill ? As the tests wont run if the service is not injected
    */
    it('should get an instance of LanguagesService', function () {
        expect(LanguagesService).toBeDefined();
    });

    it('should attach languages to the scope', function () {
        expect(scope.languages).not.toBe(null);
    });

    it('should have the same amount of languages as greetings', function () {
        expect(scope.languages.languages.length).toBe(scope.languages.greetings.length);
    });

    /*
    * Test 4: This test fails
    */
    it('should have called LanguagesService method getAll', function () {
        createController();
        expect(spy).toHaveBeenCalled();
    });

});

答案 1 :(得分:1)

为了将来参考,这就是我解决问题的方法:

我放弃的第一个测试,看起来真的有点过分了。

我放弃了第3次测试,因为它测试服务的输出而不是控制器的行为,我把这个测试移到我的服务单元测试中。

我设法通过在我的测试中模拟服务来完成第四次测试工作:

'use strict';

describe('controller: MainCtrl', function() {
  var ctrl, LanguagesService, $scope;

  beforeEach(module('fitApp'));

  beforeEach(inject(function($rootScope, $controller) {
    LanguagesService = {
      getAll: function() {}
    };

    spyOn(LanguagesService, 'getAll').and.returnValue('Foo');

    $scope = $rootScope.$new();

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

  it('should call LanguagesService.getAll() once', function() {
    expect(LanguagesService.getAll).toHaveBeenCalled();
    expect(LanguagesService.getAll.calls.count()).toEqual(1);
  });

  it('should attach languages to the scope', function() {
    expect($scope.languages).toEqual('Foo');
  });

});

两个测试都通过并测试控制器的行为。

我希望有人可以向我确认这是正确的选择