【问题标题】:Test a Controller Service Call in AngularJS在 AngularJS 中测试控制器服务调用
【发布时间】:2015-03-06 18:46:08
【问题描述】:

我对以下情况有疑问:

我有一个控制器,它执行服务调用以获取不同的可用语言及其问候语。 我想测试这个控制器,我基于我从以下网站和文章中编写的测试:

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();
});

});

【问题讨论】:

  • 您的测试有什么问题?你看到错误了吗?为了使用toHaveBeenCalled,被测对象必须是间谍。
  • 我在 beforeEach (spyOn(LanguagesService, 'getAll');) 中放了一个间谍,或者我应该把它放在另一个位置以便它工作吗?
  • 我现在明白了。不,这应该有效。 jasmine 给出的测试失败的原因是什么?
  • PhantomJS 1.9.8 (Linux) 控制器:MainCtrl 应该调用 LanguagesService 方法 getAll FAILED 预期 spy getAll 已经被调用。在 /test/spec/controllers/main.js:36

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


【解决方案1】:
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();
    });

});

【讨论】:

  • 在beforeEach方法中我定义了一个函数createController,所以如果你从测试中调用这个方法,它会被运行并调用getAll()函数。
【解决方案2】:

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

我放弃的第一个测试,看起来真的有点矫枉过正。

我也放弃了第三个测试,因为它测试的是服务的输出而不是控制器的行为,所以我将此测试移到了我的服务单元测试中。

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

'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');
  });

});

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-06-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-20
    • 2017-10-11
    • 1970-01-01
    相关资源
    最近更新 更多