【问题标题】:How do I know my controller was called, when unit testing with sinon?当使用 sinon 进行单元测试时,我怎么知道我的控制器被调用了?
【发布时间】:2017-04-17 21:18:56
【问题描述】:

Angular 单元测试的新手。我已经阅读了大量有关间谍、存根和模拟的内容,但在执行基础知识时遇到了很多麻烦:

  • 我的控制器是否正确接收我传递给构造函数的服务?
  • 是否在实例化时调用initializePage

Controller.spec(很确定以下是必需的)

    'use strict';
    describe('Controller: MainController', function() {

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

      var MainController, scope;

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

规范的其余部分:

it('should have called initializePage', function() {
        var spyInstance = sinon.spy(MainController, "initializePage");
        assert(spyInstance.called, "initializePage() was not called once");
      });
    });

我一直认为间谍就足够了,但我不确定MainController 是否正在被处决。目前spyInstance 抛出错误。 这里需要存根吗?为什么?

控制器

class MainController {
  constructor($scope, $http, $state, Session) {
    this.$scope = $scope;
    this.$state = $state;
    this.Session = Session;
    this.initializePage();
}

initializePage() {
//blah blah
}

谢谢。


修订: main.controller.spec.js
describe('Controller: MainController', function() {

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

  var mainControllerInstance, scope;

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

  it('should test the controller is in order', function() {
    assert.isFunction(mainControllerInstance.$onInit, "$onInit() has not been defined");

    sinon.spy(mainControllerInstance, "$onInit");
    assert(mainControllerInstance.$onInit.called, "$onInit() called = false");
  });




});

【问题讨论】:

    标签: angularjs unit-testing ecmascript-6 sinon chai


    【解决方案1】:

    控制器测试在 Angular 中存在一些缺陷。主要是因为不可能监视只能作为类实例使用的类的构造函数($controller(...) 的结果是)。当$controller(...) 被调用时,没有什么可窥探的,构造函数已经被调用,故事结束。

    为此,除了 Angular 模块外,还应使用 ES6/CommonJS 模块来公开控制器类并监视原型方法。由于 ES6 已经在项目中使用,所以是

    export class MainController { ... }
    

    import { MainController } from '...';
    ...
    scope = $rootScope.$new();
    sinon.spy(MainController.prototype, 'initializePage');
    mainControllerInstance = $controller('MainController', { $scope: scope });
    assert(MainController.prototype.initializePage.called);
    assert.strictEqual(mainControllerInstance.$scope, $scope);
    ...
    

    但更重要的是,initializePage 重新发明了轮子。它的工作已经由 Angular 1.5 及更高版本中的 $onInit 生命周期钩子处理。它在指令编译时自动调用,可以作为预链接函数的替代。

    $onInit 不会在控制器实例化时调用,但可以安全地假定它将在指令中,因此无需对其进行监视。它对测试更友好,测试变得

    class MainController {
      constructor($scope, $http, $state, Session) {
        this.$scope = $scope;
        this.$state = $state;
        this.Session = Session;
      }
    
      $onInit() {
        //blah blah
      }
    }
    

    scope = $rootScope.$new();
    mainControllerInstance = $controller('MainController', { $scope: scope });
    assert.isFunction(mainControllerInstance.$onInit);
    assert.strictEqual(mainControllerInstance.$scope, $scope);
    ...
    

    【讨论】:

    • 有什么东西可以阻止initalizePage变成$onInit吗?因为这就是它的用途,一种向控制器提供初始化逻辑的惯用方式,它基本上比从构造函数调用方法更可测试。至于重复声明,不确定错误指的是哪一行,但请注意,控制器实例在上面的代码中被重命名为mainControllerInstance(它是一个对象,因此不必用PascalCase命名)。跨度>
    • 不,依赖项在构造函数中也可以。 $onInit 通常被视为发生初始化的地方。有些东西(例如数据绑定)在那里可用,但在构造函数中不可用。但在这种特殊情况下,它是有益的,因为它消除了 MainController.prototype.initializePage 的技巧。 describe 行没有任何问题。很可能源映射存在问题,并且错误的位置未正确显示。
    • 谢谢,我会继续调查的!我一直在最糟糕的时候编辑 cmets,对不起,这很混乱。
    • 我现在正在使用 $onInit。所以没有办法测试服务被传递给类构造函数吗?另外..import { MainController } from './main.controller.js'; 正在引发Can't find variable: require 错误。如果你想看看它是否看起来更好,我在我的帖子中添加了一个修订。再次感谢:)
    • 这取决于测试的构建和运行方式。这个问题没有说明什么。如果是 Karma,则应正确配置。对于初学者,module(...) 必须替换为angular.mock.module(...),因为它不适用于 CommonJS 模块。至于依赖关系,它们只是mainControllerInstance 上的断言,例如我已经更新了答案。
    猜你喜欢
    • 2014-11-23
    • 1970-01-01
    • 2019-05-24
    • 1970-01-01
    • 1970-01-01
    • 2021-12-01
    • 1970-01-01
    • 2023-04-01
    相关资源
    最近更新 更多