4

类似的问题:Jasmine angularjs - 监视初始化控制器时调用的方法

在我的控制器中,我使用 angular-local-storage 包通过注入提供 localStorageService。

在我的单元测试中,我想确保从服务中检索数据,所以我监视“get”和“add”方法并模拟它们(.andCallFake)。

这适用于通过 $scope.$watch 调用的所有方法——只要我强制使用 $digest。但是对于控制器初始化时调用的方法,它似乎不起作用。谁能告诉我为什么这不起作用?

应用>Main.js

angular.module('angularTodoApp')
  .controller('MainCtrl',['$scope','localStorageService',function ($scope, localStorageService) {

    var todosInStore =  localStorageService.get('todos');
    $scope.todos = todosInStore && todosInStore.split('\n') || [];
    //$scope.todos = ['Item 1', 'Item 2', 'Item 3'];

    $scope.$watch('todos', function(){
      localStorageService.add('todos', $scope.todos.join('\n'));
    },true);

    $scope.addTodo  = function() {
      $scope.todos.push($scope.todo);
      $scope.todo = '';
    };

    $scope.removeTodo = function(index) {
      $scope.todos.splice(index,1);
    };
  }]);

测试>Main.js

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

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

  var MainCtrl,
    scope,
    localStorageService,
    store = [];

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

    //mock localStorageService get/add
    spyOn(localStorageService,'get').andCallFake(function(key){
      return store[key];
    });
    spyOn(localStorageService,'add').andCallFake(function(key, val){
      store[key] = val;
    });
  }));

  it('should retrieve "todos" from the store and assign to scope', function () {
    expect(localStorageService.get).toHaveBeenCalledWith('todos');
    expect(scope.todos.length).toBe(0);
  });

  it('should add items to the list and update the store for key = "todos"', function () {
    scope.todo = 'Test 1';
    scope.addTodo();
    scope.$digest();
    expect(localStorageService.add).toHaveBeenCalledWith('todos', jasmine.any(String));
    expect(scope.todos.length).toBe(1);
  });

除了构造函数中的测试之外,所有测试都通过:

expect(localStorageService.get).toHaveBeenCalledWith('todos');
4

3 回答 3

5

原因是控制器在您调用的点初始化$controller,在您的示例中是您通过创建间谍之前spyOn。您的示例的解决方案是将调用移动$controller到调用之后spyOn

对于更长的测试套件,为了保持 DRY,您可能必须将调用$controller放在一个单独的函数中,然后您可以在模拟任何所需的服务后调用该函数。

于 2014-04-14T05:01:55.367 回答
3

更新了测试套件,在调用控制器构造函数之前确保模拟已经到位:

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

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

  var MainCtrl,
    scope,
    localStorageService,
    store;

  // Initialize the controller and mocks
  beforeEach(inject(function ($controller, $rootScope, _localStorageService_) {
    store = []; //clear the store before each test
    scope = $rootScope.$new();
    localStorageService = _localStorageService_;

    //mock localStorageService get/add
    spyOn(localStorageService,'get').andCallFake(function(key){
      return store[key];
    });
    spyOn(localStorageService,'add').andCallFake(function(key, val){
      store[key] = val;
    });

    //Instantiate controller to test
    MainCtrl = $controller('MainCtrl', {
      $scope: scope,
      localStorageService: localStorageService
    });
  }));

  it('should retrieve "todos" from the store and assign to scope', function () {
    expect(localStorageService.get).toHaveBeenCalledWith('todos');
    expect(scope.todos.length).toBe(0);
  });

  it('should add items to the list and update the store for key = "todos"', function () {
    scope.todo = 'Test 1';
    scope.addTodo();
    scope.$digest();
    expect(localStorageService.add).toHaveBeenCalledWith('todos', jasmine.any(String));
    expect(scope.todos.length).toBe(1);
  });

  it('should remove items to the list and update the store', function() {
    scope.todo = 'Test 1';
    scope.addTodo();
    scope.$digest();
    //reset call count
    localStorageService.add.reset();

    scope.removeTodo(0);
    scope.$digest();
    expect(localStorageService.add).toHaveBeenCalledWith('todos', jasmine.any(String));
    expect(scope.todos.length).toBe(0);
  });
});
于 2014-04-14T05:29:25.343 回答
1

您还需要在 beforeEach() 调用中包含 LocalStorageModule 以在 test.js 文件中加载模块。

beforeEach(module('angularTodoApp', 'LocalStorageModule'));

另外,不要忘记更新您的 karma.conf.js 文件

module.exports = function(config) {
    // ...
    files: [
        '/path/to/angular.min.js',
        '/path/to/angular-mocks.js',
        '/path/to/angular-local-storage.js',
        'Main/test.js',
        'Main/app.js'
    ],
    //...

我只是偶然发现了同样的问题,这对我有用。

spyOn(...).andCallFake(...)最后,如果你使用 Jasmine 2.4 ,你还需要修改调用spyOn(...).and.callFake(...)

前面的答案中给出的方法在 Jasmine 2.4 中不起作用(虽然在以前的版本中没有测试过)。

于 2016-05-09T18:10:57.527 回答