当使用sinon进行单元测试时,如何知道我的控制器被调用?

时间:2016-12-02 21:28:23

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

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




});

1 个答案:

答案 0 :(得分: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);
...