【问题标题】:Jasmine angularjs - spying on angular-local-storage methods called when controller is initializedJasmine angularjs - 监视初始化控制器时调用的 angular-local-storage 方法
【发布时间】:2014-04-13 22:10:12
【问题描述】:

类似问题:Jasmine angularjs - spying on a method that is called when controller is initialized

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

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

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

app>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);
    };
  }]);

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

【问题讨论】:

    标签: javascript angularjs jasmine angular-local-storage


    【解决方案1】:

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

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

    【讨论】:

    • 是的,我醒来时才意识到。上帝啊,熬夜并试图弄清楚这件事从来都不是一件好事!谢谢!接受,赞成。
    【解决方案2】:

    更新了测试套件,确保在调用控制器构造函数之前准备好模拟:

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

    【讨论】:

    • 我在你的代码中做了精确的修改,现在出现错误Controller: MainCtrl should add items to the list and update the store for key = "todos" FAILED [native code] })> ] but it was never called. Error: Expected spy add to have been called with [ 'todos', <jasmine.any(function String() { at D:/js-app/apps/mytodo/test/spec/controllers/main.js:41你知道为什么会这样吗?
    • 你能把你的代码放在一个要点中吗?我一直忙于 Docker 和基础架构,并将 Angular 编码交给了一位同事,所以除了概述之外,我已经有将近一年没有使用它了.... - 但我可以尝试提供帮助。
    • 好的,您的帮助提供,我已经找到了答案。我在某处发现,我应该添加超时,因为似乎在模拟服务模拟之前有一段时间。现在效果很好
    • 如果您将代码作为答案发布,我将接受它作为未来访问者的答案
    【解决方案3】:

    您还需要在 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'
        ],
        //...
    

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多